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_commit.h>
33#include <drc/drc_engine.h>
35#include <drc/drc_item.h>
36#include <drc/drc_report.h>
39#include <footprint.h>
41#include <jobs/job_fp_upgrade.h>
57#include <jobs/job_pcb_render.h>
58#include <jobs/job_pcb_drc.h>
59#include <jobs/job_pcb_import.h>
61#include <eda_units.h>
63#include <lset.h>
64#include <cli/exit_codes.h>
70#include <tool/tool_manager.h>
71#include <tools/drc_tool.h>
72#include <filename_resolver.h>
77#include <kiface_base.h>
78#include <macros.h>
79#include <pad.h>
80#include <pcb_marker.h>
84#include <kiface_ids.h>
87#include <pcbnew_settings.h>
88#include <pcbplot.h>
89#include <pcb_plotter.h>
90#include <pcb_edit_frame.h>
91#include <pcb_track.h>
92#include <pgm_base.h>
95#include <project_pcb.h>
97#include <reporter.h>
98#include <progress_reporter.h>
100#include <export_vrml.h>
101#include <kiplatform/io.h>
108#include <dialogs/dialog_plot.h>
113#include <paths.h>
115
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
423 if( !Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpen() )
424 {
425 wxString pcbPath = aPath;
426
427 if( pcbPath.IsEmpty() )
428 {
429 wxFileName path = Pgm().GetSettingsManager().Prj().GetProjectFullName();
431 path.MakeAbsolute();
432 pcbPath = path.GetFullPath();
433 }
434
435 if( !m_cliBoard )
436 m_cliBoard = LoadBoard( pcbPath, true );
437
438 brd = m_cliBoard;
439 }
440 else if( Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpen() )
441 {
442 PCB_EDIT_FRAME* editFrame = (PCB_EDIT_FRAME*) m_kiway->Player( FRAME_PCB_EDITOR, false );
443
444 if( editFrame )
445 brd = editFrame->GetBoard();
446 }
447 else
448 {
449 brd = LoadBoard( aPath, true );
450 }
451
452 if( !brd )
453 m_reporter->Report( _( "Failed to load board\n" ), RPT_SEVERITY_ERROR );
454
455 return brd;
456}
457
458
459LSEQ PCBNEW_JOBS_HANDLER::convertLayerArg( wxString& aLayerString, BOARD* aBoard ) const
460{
461 std::map<wxString, LSET> layerUserMasks;
462 std::map<wxString, LSET> layerMasks;
463 std::map<wxString, LSET> layerGuiMasks;
464
465 // Build list of layer names and their layer mask:
466 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
467 {
468 // Add user layer name
469 if( aBoard )
470 layerUserMasks[ aBoard->GetLayerName( layer ) ] = LSET( { layer } );
471
472 // Add layer name used in pcb files
473 layerMasks[ LSET::Name( layer ) ] = LSET( { layer } );
474 // Add layer name using GUI canonical layer name
475 layerGuiMasks[ LayerName( layer ) ] = LSET( { layer } );
476 }
477
478 // Add list of grouped layer names used in pcb files
479 layerMasks[ wxT( "*" ) ] = LSET::AllLayersMask();
480 layerMasks[ wxT( "*.Cu" ) ] = LSET::AllCuMask();
481 layerMasks[ wxT( "*In.Cu" ) ] = LSET::InternalCuMask();
482 layerMasks[ wxT( "F&B.Cu" ) ] = LSET( { F_Cu, B_Cu } );
483 layerMasks[ wxT( "*.Adhes" ) ] = LSET( { B_Adhes, F_Adhes } );
484 layerMasks[ wxT( "*.Paste" ) ] = LSET( { B_Paste, F_Paste } );
485 layerMasks[ wxT( "*.Mask" ) ] = LSET( { B_Mask, F_Mask } );
486 layerMasks[ wxT( "*.SilkS" ) ] = LSET( { B_SilkS, F_SilkS } );
487 layerMasks[ wxT( "*.Fab" ) ] = LSET( { B_Fab, F_Fab } );
488 layerMasks[ wxT( "*.CrtYd" ) ] = LSET( { B_CrtYd, F_CrtYd } );
489
490 // Add list of grouped layer names using GUI canonical layer names
491 layerGuiMasks[ wxT( "*.Adhesive" ) ] = LSET( { B_Adhes, F_Adhes } );
492 layerGuiMasks[ wxT( "*.Silkscreen" ) ] = LSET( { B_SilkS, F_SilkS } );
493 layerGuiMasks[ wxT( "*.Courtyard" ) ] = LSET( { B_CrtYd, F_CrtYd } );
494
495 LSEQ layerMask;
496
497 auto pushLayers =
498 [&]( const LSET& layerSet )
499 {
500 for( PCB_LAYER_ID layer : layerSet.Seq() )
501 layerMask.push_back( layer );
502 };
503
504 if( !aLayerString.IsEmpty() )
505 {
506 wxStringTokenizer layerTokens( aLayerString, "," );
507
508 while( layerTokens.HasMoreTokens() )
509 {
510 std::string token = TO_UTF8( layerTokens.GetNextToken().Trim( true ).Trim( false ) );
511
512 if( layerUserMasks.contains( token ) )
513 pushLayers( layerUserMasks.at( token ) );
514 else if( layerMasks.count( token ) )
515 pushLayers( layerMasks.at( token ) );
516 else if( layerGuiMasks.count( token ) )
517 pushLayers( layerGuiMasks.at( token ) );
518 else
519 m_reporter->Report( wxString::Format( _( "Invalid layer name '%s'\n" ), token ) );
520 }
521 }
522
523 return layerMask;
524}
525
526
528{
529 JOB_EXPORT_PCB_3D* aStepJob = dynamic_cast<JOB_EXPORT_PCB_3D*>( aJob );
530
531 if( aStepJob == nullptr )
533
534 BOARD* brd = getBoard( aStepJob->m_filename );
535
536 if( !brd )
538
539 if( !aStepJob->m_variant.IsEmpty() )
540 brd->SetCurrentVariant( aStepJob->m_variant );
541
542 if( aStepJob->GetConfiguredOutputPath().IsEmpty() )
543 {
544 wxFileName fn = brd->GetFileName();
545 fn.SetName( fn.GetName() );
546
547 switch( aStepJob->m_format )
548 {
558 default:
559 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
560 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
561 }
562
563 aStepJob->SetWorkingOutputPath( fn.GetFullName() );
564 }
565
566 wxString outPath = resolveJobOutputPath( aJob, brd );
567
568 if( !PATHS::EnsurePathExists( outPath, true ) )
569 {
570 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
572 }
573
575 {
576
577 double scale = 0.0;
578 switch ( aStepJob->m_vrmlUnits )
579 {
580 case JOB_EXPORT_PCB_3D::VRML_UNITS::MM: scale = 1.0; break;
581 case JOB_EXPORT_PCB_3D::VRML_UNITS::METERS: scale = 0.001; break;
582 case JOB_EXPORT_PCB_3D::VRML_UNITS::TENTHS: scale = 10.0 / 25.4; break;
583 case JOB_EXPORT_PCB_3D::VRML_UNITS::INCH: scale = 1.0 / 25.4; break;
584 }
585
586 EXPORTER_VRML vrmlExporter( brd );
587 wxString messages;
588
589 double originX = pcbIUScale.IUTomm( aStepJob->m_3dparams.m_Origin.x );
590 double originY = pcbIUScale.IUTomm( aStepJob->m_3dparams.m_Origin.y );
591
592 if( !aStepJob->m_hasUserOrigin )
593 {
594 BOX2I bbox = brd->ComputeBoundingBox( true, true );
595 originX = pcbIUScale.IUTomm( bbox.GetCenter().x );
596 originY = pcbIUScale.IUTomm( bbox.GetCenter().y );
597 }
598
599 bool success = vrmlExporter.ExportVRML_File( brd->GetProject(),
600 &messages,
601 outPath,
602 scale,
604 aStepJob->m_3dparams.m_IncludeDNP,
605 !aStepJob->m_vrmlModelDir.IsEmpty(),
606 aStepJob->m_vrmlRelativePaths,
607 aStepJob->m_vrmlModelDir,
608 originX,
609 originY );
610
611 if ( success )
612 {
613 m_reporter->Report( wxString::Format( _( "Successfully exported VRML to %s" ),
614 outPath ),
616 }
617 else
618 {
619 m_reporter->Report( _( "Error exporting VRML" ), RPT_SEVERITY_ERROR );
621 }
622 }
623 else
624 {
625 EXPORTER_STEP_PARAMS params = aStepJob->m_3dparams;
626
627 switch( aStepJob->m_format )
628 {
638 default:
639 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
640 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
641 }
642
643 EXPORTER_STEP stepExporter( brd, params, m_reporter );
644 stepExporter.m_outputFile = aStepJob->GetFullOutputPath( brd->GetProject() );
645
646 if( !stepExporter.Export() )
648 }
649
650 return CLI::EXIT_CODES::OK;
651}
652
653
655{
656 JOB_PCB_RENDER* aRenderJob = dynamic_cast<JOB_PCB_RENDER*>( aJob );
657
658 if( aRenderJob == nullptr )
660
661 // Reject width and height being invalid
662 // Final bit of sanity because this can blow things up
663 if( aRenderJob->m_width <= 0 || aRenderJob->m_height <= 0 )
664 {
665 m_reporter->Report( _( "Invalid image dimensions" ), RPT_SEVERITY_ERROR );
667 }
668
669 BOARD* brd = getBoard( aRenderJob->m_filename );
670
671 if( !brd )
673
674 if( aRenderJob->GetConfiguredOutputPath().IsEmpty() )
675 {
676 wxFileName fn = brd->GetFileName();
677
678 switch( aRenderJob->m_format )
679 {
682 default:
683 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
684 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
685 }
686
687 // set the name to board name + "side", its lazy but its hard to generate anything truely unique
688 // incase someone is doing this in a jobset with multiple jobs, they should be setting the output themselves
689 // or we do a hash based on all the options
690 fn.SetName( wxString::Format( "%s-%d", fn.GetName(), static_cast<int>( aRenderJob->m_side ) ) );
691
692 aRenderJob->SetWorkingOutputPath( fn.GetFullName() );
693 }
694
695 wxString outPath = resolveJobOutputPath( aJob, brd );
696
697 if( !PATHS::EnsurePathExists( outPath, true ) )
698 {
699 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
701 }
702
703 BOARD_ADAPTER boardAdapter;
704
705 boardAdapter.SetBoard( brd );
706 boardAdapter.m_IsBoardView = false;
707
709
711 {
712 cfg.m_Render = userCfg->m_Render;
713 cfg.m_Camera = userCfg->m_Camera;
714 cfg.m_LayerPresets = userCfg->m_LayerPresets;
715 }
716
717 if( aRenderJob->m_appearancePreset.empty() )
718 {
719 // Force display 3D models
721 cfg.m_Render.show_footprints_dnp = true;
725 }
726
727 if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::BASIC )
728 {
729 // Silkscreen is pixelated without antialiasing
731
732 cfg.m_Render.raytrace_backfloor = aRenderJob->m_floor;
733 cfg.m_Render.raytrace_post_processing = aRenderJob->m_floor;
734
736 cfg.m_Render.raytrace_reflections = false;
737 cfg.m_Render.raytrace_shadows = aRenderJob->m_floor;
738
739 // Better colors
741
742 // Tracks below soldermask are not visible without refractions
745 }
746 else if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::HIGH )
747 {
749 cfg.m_Render.raytrace_backfloor = true;
753 cfg.m_Render.raytrace_shadows = true;
756 }
757 else if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::JOB_SETTINGS )
758 {
760 cfg.m_Render.raytrace_backfloor = aRenderJob->m_floor;
763 }
764
766 aRenderJob->m_lightTopIntensity.y,
767 aRenderJob->m_lightTopIntensity.z, 1.0 );
768
770 aRenderJob->m_lightBottomIntensity.y,
771 aRenderJob->m_lightBottomIntensity.z, 1.0 );
772
774 aRenderJob->m_lightCameraIntensity.y,
775 aRenderJob->m_lightCameraIntensity.z, 1.0 );
776
777 COLOR4D lightColor( aRenderJob->m_lightSideIntensity.x,
778 aRenderJob->m_lightSideIntensity.y,
779 aRenderJob->m_lightSideIntensity.z, 1.0 );
780
782 lightColor, lightColor, lightColor, lightColor,
783 lightColor, lightColor, lightColor, lightColor,
784 };
785
786 int sideElevation = aRenderJob->m_lightSideElevation;
787
789 sideElevation, sideElevation, sideElevation, sideElevation,
790 -sideElevation, -sideElevation, -sideElevation, -sideElevation,
791 };
792
794 45, 135, 225, 315, 45, 135, 225, 315,
795 };
796
797 cfg.m_CurrentPreset = aRenderJob->m_appearancePreset;
799 boardAdapter.m_Cfg = &cfg;
800
801 // Apply the preset's layer visibility and colors to the render settings
802 if( !aRenderJob->m_appearancePreset.empty() )
803 {
804 wxString presetName = wxString::FromUTF8( aRenderJob->m_appearancePreset );
805
806 if( presetName == FOLLOW_PCB || presetName == FOLLOW_PLOT_SETTINGS )
807 {
808 boardAdapter.SetVisibleLayers( boardAdapter.GetVisibleLayers() );
809 }
810 else if( LAYER_PRESET_3D* preset = cfg.FindPreset( presetName ) )
811 {
812 boardAdapter.SetVisibleLayers( preset->layers );
813 boardAdapter.SetLayerColors( preset->colors );
814
815 if( preset->name.Lower() == _( "legacy colors" ) )
816 cfg.m_UseStackupColors = false;
817 }
818 }
819
822 && aRenderJob->m_format == JOB_PCB_RENDER::FORMAT::PNG ) )
823 {
824 boardAdapter.m_ColorOverrides[LAYER_3D_BACKGROUND_TOP] = COLOR4D( 1.0, 1.0, 1.0, 0.0 );
825 boardAdapter.m_ColorOverrides[LAYER_3D_BACKGROUND_BOTTOM] = COLOR4D( 1.0, 1.0, 1.0, 0.0 );
826 }
827
829
830 static std::map<JOB_PCB_RENDER::SIDE, VIEW3D_TYPE> s_viewCmdMap = {
837 };
838
841
842 wxSize windowSize( aRenderJob->m_width, aRenderJob->m_height );
843 TRACK_BALL camera( 2 * RANGE_SCALE_3D );
844
845 camera.SetProjection( projection );
846 camera.SetCurWindowSize( windowSize );
847
848 RENDER_3D_RAYTRACE_RAM raytrace( boardAdapter, camera );
849 raytrace.SetCurWindowSize( windowSize );
850
851 for( bool first = true; raytrace.Redraw( false, m_reporter, m_reporter ); first = false )
852 {
853 if( first )
854 {
855 const float cmTo3D = boardAdapter.BiuTo3dUnits() * pcbIUScale.mmToIU( 10.0 );
856
857 // First redraw resets lookat point to the board center, so set up the camera here
858 camera.ViewCommand_T1( s_viewCmdMap[aRenderJob->m_side] );
859
860 camera.SetLookAtPos_T1( camera.GetLookAtPos_T1() + SFVEC3F( aRenderJob->m_pivot.x,
861 aRenderJob->m_pivot.y,
862 aRenderJob->m_pivot.z ) * cmTo3D );
863
864 camera.Pan_T1( SFVEC3F( aRenderJob->m_pan.x, aRenderJob->m_pan.y, aRenderJob->m_pan.z ) );
865
866 camera.Zoom_T1( aRenderJob->m_zoom );
867
868 camera.RotateX_T1( DEG2RAD( aRenderJob->m_rotation.x ) );
869 camera.RotateY_T1( DEG2RAD( aRenderJob->m_rotation.y ) );
870 camera.RotateZ_T1( DEG2RAD( aRenderJob->m_rotation.z ) );
871
872 camera.Interpolate( 1.0f );
873 camera.SetT0_and_T1_current_T();
874 camera.ParametersChanged();
875 }
876 }
877
878 uint8_t* rgbaBuffer = raytrace.GetBuffer();
879 wxSize realSize = raytrace.GetRealBufferSize();
880 bool success = !!rgbaBuffer;
881
882 if( rgbaBuffer )
883 {
884 const unsigned int wxh = realSize.x * realSize.y;
885
886 unsigned char* rgbBuffer = (unsigned char*) malloc( wxh * 3 );
887 unsigned char* alphaBuffer = (unsigned char*) malloc( wxh );
888
889 unsigned char* rgbaPtr = rgbaBuffer;
890 unsigned char* rgbPtr = rgbBuffer;
891 unsigned char* alphaPtr = alphaBuffer;
892
893 for( int y = 0; y < realSize.y; y++ )
894 {
895 for( int x = 0; x < realSize.x; x++ )
896 {
897 rgbPtr[0] = rgbaPtr[0];
898 rgbPtr[1] = rgbaPtr[1];
899 rgbPtr[2] = rgbaPtr[2];
900 alphaPtr[0] = rgbaPtr[3];
901
902 rgbaPtr += 4;
903 rgbPtr += 3;
904 alphaPtr += 1;
905 }
906 }
907
908 wxImage image( realSize );
909 image.SetData( rgbBuffer );
910 image.SetAlpha( alphaBuffer );
911 image = image.Mirror( false );
912
913 image.SetOption( wxIMAGE_OPTION_QUALITY, 90 );
914 image.SaveFile( outPath, aRenderJob->m_format == JOB_PCB_RENDER::FORMAT::PNG ? wxBITMAP_TYPE_PNG
915 : wxBITMAP_TYPE_JPEG );
916 }
917
918 if( success )
919 {
920 m_reporter->Report( _( "Successfully created 3D render image" ) + wxS( "\n" ), RPT_SEVERITY_INFO );
921 return CLI::EXIT_CODES::OK;
922 }
923 else
924 {
925 m_reporter->Report( _( "Error creating 3D render image" ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
927 }
928}
929
930
932{
933 JOB_EXPORT_PCB_SVG* aSvgJob = dynamic_cast<JOB_EXPORT_PCB_SVG*>( aJob );
934
935 if( aSvgJob == nullptr )
937
938 BOARD* brd = getBoard( aSvgJob->m_filename );
939 TOOL_MANAGER* toolManager = getToolManager( brd );
940
941 if( !brd )
943
944 if( !aSvgJob->m_variant.IsEmpty() )
945 brd->SetCurrentVariant( aSvgJob->m_variant );
946
948 {
949 if( aSvgJob->GetConfiguredOutputPath().IsEmpty() )
950 {
951 wxFileName fn = brd->GetFileName();
952 fn.SetName( fn.GetName() );
954
955 aSvgJob->SetWorkingOutputPath( fn.GetFullName() );
956 }
957 }
958
959 wxString outPath = resolveJobOutputPath( aJob, brd, &aSvgJob->m_drawingSheet );
960
962 {
963 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
965 }
966
967 if( aSvgJob->m_checkZonesBeforePlot )
968 {
969 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
970 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
971
972 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
973 }
974
975 if( aSvgJob->m_argLayers )
976 aSvgJob->m_plotLayerSequence = convertLayerArg( aSvgJob->m_argLayers.value(), brd );
977
978 if( aSvgJob->m_argCommonLayers )
979 aSvgJob->m_plotOnAllLayersSequence = convertLayerArg( aSvgJob->m_argCommonLayers.value(), brd );
980
981 if( aSvgJob->m_plotLayerSequence.size() < 1 )
982 {
983 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
985 }
986
987 PCB_PLOT_PARAMS plotOpts;
988 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aSvgJob, *m_reporter );
989
990 PCB_PLOTTER plotter( brd, m_reporter, plotOpts );
991
992 std::optional<wxString> layerName;
993 std::optional<wxString> sheetName;
994 std::optional<wxString> sheetPath;
995
997 {
998 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
999 layerName = aSvgJob->GetVarOverrides().at( wxT( "LAYER" ) );
1000
1001 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1002 sheetName = aSvgJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1003
1004 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1005 sheetPath = aSvgJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1006 }
1007
1008 if( !plotter.Plot( outPath, aSvgJob->m_plotLayerSequence, aSvgJob->m_plotOnAllLayersSequence,
1010 layerName, sheetName, sheetPath ) )
1011 {
1013 }
1014
1015 return CLI::EXIT_CODES::OK;
1016}
1017
1018
1020{
1021 JOB_EXPORT_PCB_DXF* aDxfJob = dynamic_cast<JOB_EXPORT_PCB_DXF*>( aJob );
1022
1023 if( aDxfJob == nullptr )
1025
1026 BOARD* brd = getBoard( aDxfJob->m_filename );
1027
1028 if( !brd )
1030
1031 if( !aDxfJob->m_variant.IsEmpty() )
1032 brd->SetCurrentVariant( aDxfJob->m_variant );
1033
1034 TOOL_MANAGER* toolManager = getToolManager( brd );
1035
1036 if( aDxfJob->m_checkZonesBeforePlot )
1037 {
1038 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1039 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1040
1041 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1042 }
1043
1044 if( aDxfJob->m_argLayers )
1045 aDxfJob->m_plotLayerSequence = convertLayerArg( aDxfJob->m_argLayers.value(), brd );
1046
1047 if( aDxfJob->m_argCommonLayers )
1048 aDxfJob->m_plotOnAllLayersSequence = convertLayerArg( aDxfJob->m_argCommonLayers.value(), brd );
1049
1050 if( aDxfJob->m_plotLayerSequence.size() < 1 )
1051 {
1052 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1054 }
1055
1057 {
1058 if( aDxfJob->GetConfiguredOutputPath().IsEmpty() )
1059 {
1060 wxFileName fn = brd->GetFileName();
1061 fn.SetName( fn.GetName() );
1063
1064 aDxfJob->SetWorkingOutputPath( fn.GetFullName() );
1065 }
1066 }
1067
1068 wxString outPath = resolveJobOutputPath( aJob, brd, &aDxfJob->m_drawingSheet );
1069
1071 {
1072 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1074 }
1075
1076 PCB_PLOT_PARAMS plotOpts;
1077 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aDxfJob, *m_reporter);
1078
1079 PCB_PLOTTER plotter( brd, m_reporter, plotOpts );
1080
1081 std::optional<wxString> layerName;
1082 std::optional<wxString> sheetName;
1083 std::optional<wxString> sheetPath;
1084
1086 {
1087 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1088 layerName = aDxfJob->GetVarOverrides().at( wxT( "LAYER" ) );
1089
1090 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1091 sheetName = aDxfJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1092
1093 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1094 sheetPath = aDxfJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1095 }
1096
1097 if( !plotter.Plot( outPath, aDxfJob->m_plotLayerSequence, aDxfJob->m_plotOnAllLayersSequence,
1099 layerName, sheetName, sheetPath ) )
1100 {
1102 }
1103
1104 return CLI::EXIT_CODES::OK;
1105}
1106
1107
1109{
1110 bool plotAllLayersOneFile = false;
1111 JOB_EXPORT_PCB_PDF* pdfJob = dynamic_cast<JOB_EXPORT_PCB_PDF*>( aJob );
1112
1113 if( pdfJob == nullptr )
1115
1116 BOARD* brd = getBoard( pdfJob->m_filename );
1117
1118 if( !brd )
1120
1121 if( !pdfJob->m_variant.IsEmpty() )
1122 brd->SetCurrentVariant( pdfJob->m_variant );
1123
1124 TOOL_MANAGER* toolManager = getToolManager( brd );
1125
1126 if( pdfJob->m_checkZonesBeforePlot )
1127 {
1128 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1129 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1130
1131 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1132 }
1133
1134 if( pdfJob->m_argLayers )
1135 pdfJob->m_plotLayerSequence = convertLayerArg( pdfJob->m_argLayers.value(), brd );
1136
1137 if( pdfJob->m_argCommonLayers )
1138 pdfJob->m_plotOnAllLayersSequence = convertLayerArg( pdfJob->m_argCommonLayers.value(), brd );
1139
1141 plotAllLayersOneFile = true;
1142
1143 if( pdfJob->m_plotLayerSequence.size() < 1 )
1144 {
1145 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1147 }
1148
1149 const bool outputIsSingle = plotAllLayersOneFile || pdfJob->m_pdfSingle;
1150
1151 if( outputIsSingle && pdfJob->GetConfiguredOutputPath().IsEmpty() )
1152 {
1153 wxFileName fn = brd->GetFileName();
1154 fn.SetName( fn.GetName() );
1156
1157 pdfJob->SetWorkingOutputPath( fn.GetFullName() );
1158 }
1159
1160 wxString outPath = resolveJobOutputPath( pdfJob, brd, &pdfJob->m_drawingSheet );
1161
1162 PCB_PLOT_PARAMS plotOpts;
1163 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, pdfJob, *m_reporter );
1164
1165 PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts );
1166
1167 if( !PATHS::EnsurePathExists( outPath, outputIsSingle ) )
1168 {
1169 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1171 }
1172
1173 std::optional<wxString> layerName;
1174 std::optional<wxString> sheetName;
1175 std::optional<wxString> sheetPath;
1176
1177 if( plotAllLayersOneFile )
1178 {
1179 if( pdfJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1180 layerName = pdfJob->GetVarOverrides().at( wxT( "LAYER" ) );
1181
1182 if( pdfJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1183 sheetName = pdfJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1184
1185 if( pdfJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1186 sheetPath = pdfJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1187 }
1188
1189 if( !pcbPlotter.Plot( outPath, pdfJob->m_plotLayerSequence,
1190 pdfJob->m_plotOnAllLayersSequence, false, outputIsSingle,
1191 layerName, sheetName, sheetPath ) )
1192 {
1194 }
1195
1196 return CLI::EXIT_CODES::OK;
1197}
1198
1199
1201{
1202 JOB_EXPORT_PCB_PS* psJob = dynamic_cast<JOB_EXPORT_PCB_PS*>( aJob );
1203
1204 if( psJob == nullptr )
1206
1207 BOARD* brd = getBoard( psJob->m_filename );
1208
1209 if( !brd )
1211
1212 if( !psJob->m_variant.IsEmpty() )
1213 brd->SetCurrentVariant( psJob->m_variant );
1214
1215 TOOL_MANAGER* toolManager = getToolManager( brd );
1216
1217 if( psJob->m_checkZonesBeforePlot )
1218 {
1219 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1220 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1221
1222 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1223 }
1224
1225 if( psJob->m_argLayers )
1226 psJob->m_plotLayerSequence = convertLayerArg( psJob->m_argLayers.value(), brd );
1227
1228 if( psJob->m_argCommonLayers )
1229 psJob->m_plotOnAllLayersSequence = convertLayerArg( psJob->m_argCommonLayers.value(), brd );
1230
1231 if( psJob->m_plotLayerSequence.size() < 1 )
1232 {
1233 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1235 }
1236
1237 bool isSingle = psJob->m_genMode == JOB_EXPORT_PCB_PS::GEN_MODE::SINGLE;
1238
1239 if( isSingle )
1240 {
1241 if( psJob->GetConfiguredOutputPath().IsEmpty() )
1242 {
1243 wxFileName fn = brd->GetFileName();
1244 fn.SetName( fn.GetName() );
1246
1247 psJob->SetWorkingOutputPath( fn.GetFullName() );
1248 }
1249 }
1250
1251 wxString outPath = resolveJobOutputPath( psJob, brd, &psJob->m_drawingSheet );
1252
1253 if( !PATHS::EnsurePathExists( outPath, isSingle ) )
1254 {
1255 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1257 }
1258
1259 PCB_PLOT_PARAMS plotOpts;
1260 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, psJob, *m_reporter );
1261
1262 PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts );
1263
1264 std::optional<wxString> layerName;
1265 std::optional<wxString> sheetName;
1266 std::optional<wxString> sheetPath;
1267
1268 if( isSingle )
1269 {
1270 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1271 layerName = psJob->GetVarOverrides().at( wxT( "LAYER" ) );
1272
1273 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1274 sheetName = psJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1275
1276 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1277 sheetPath = psJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1278 }
1279
1280 if( !pcbPlotter.Plot( outPath, psJob->m_plotLayerSequence, psJob->m_plotOnAllLayersSequence, false, isSingle,
1281 layerName, sheetName, sheetPath ) )
1282 {
1284 }
1285
1286 return CLI::EXIT_CODES::OK;
1287}
1288
1289
1291{
1292 int exitCode = CLI::EXIT_CODES::OK;
1293 JOB_EXPORT_PCB_GERBERS* aGerberJob = dynamic_cast<JOB_EXPORT_PCB_GERBERS*>( aJob );
1294
1295 if( aGerberJob == nullptr )
1297
1298 BOARD* brd = getBoard( aGerberJob->m_filename );
1299
1300 if( !brd )
1302
1303 if( !aGerberJob->m_variant.IsEmpty() )
1304 brd->SetCurrentVariant( aGerberJob->m_variant );
1305
1306 wxString outPath = resolveJobOutputPath( aJob, brd, &aGerberJob->m_drawingSheet );
1307
1308 if( !PATHS::EnsurePathExists( outPath, false ) )
1309 {
1310 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1312 }
1313
1314 TOOL_MANAGER* toolManager = getToolManager( brd );
1315
1316 if( aGerberJob->m_checkZonesBeforePlot )
1317 {
1318 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1319 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1320
1321 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1322 }
1323
1324 bool hasLayerListSpecified = false; // will be true if the user layer list is not empty
1325
1326 if( aGerberJob->m_argLayers )
1327 {
1328 if( !aGerberJob->m_argLayers.value().empty() )
1329 {
1330 aGerberJob->m_plotLayerSequence = convertLayerArg( aGerberJob->m_argLayers.value(), brd );
1331 hasLayerListSpecified = true;
1332 }
1333 else
1334 {
1336 }
1337 }
1338
1339 if( aGerberJob->m_argCommonLayers )
1340 aGerberJob->m_plotOnAllLayersSequence = convertLayerArg( aGerberJob->m_argCommonLayers.value(), brd );
1341
1342 PCB_PLOT_PARAMS boardPlotOptions = brd->GetPlotOptions();
1343 GERBER_JOBFILE_WRITER jobfile_writer( brd );
1344
1345 wxString fileExt;
1346
1347 if( aGerberJob->m_useBoardPlotParams )
1348 {
1349 // The board plot options are saved with all copper layers enabled, even those that don't
1350 // exist in the current stackup. This is done so the layers are automatically enabled in the plot
1351 // dialog when the user enables them. We need to filter out these not-enabled layers here so
1352 // we don't plot 32 layers when we only have 4, etc.
1353 LSET plotLayers = ( boardPlotOptions.GetLayerSelection() & LSET::AllNonCuMask() )
1354 | ( brd->GetEnabledLayers() & LSET::AllCuMask() );
1355 aGerberJob->m_plotLayerSequence = plotLayers.SeqStackupForPlotting();
1356 aGerberJob->m_plotOnAllLayersSequence = boardPlotOptions.GetPlotOnAllLayersSequence();
1357 }
1358 else
1359 {
1360 // default to the board enabled layers, but only if the user has not specifed a layer list
1361 // ( m_plotLayerSequence can be empty with a broken user layer list)
1362 if( aGerberJob->m_plotLayerSequence.empty() && !hasLayerListSpecified )
1364 }
1365
1366 // Ensure layers to plot are restricted to enabled layers of the board to plot
1367 LSET layersToPlot = LSET( { aGerberJob->m_plotLayerSequence } ) & brd->GetEnabledLayers();
1368
1369 for( PCB_LAYER_ID layer : layersToPlot.UIOrder() )
1370 {
1371 LSEQ plotSequence;
1372
1373 // Base layer always gets plotted first.
1374 plotSequence.push_back( layer );
1375
1376 // Now all the "include on all" layers
1377 for( PCB_LAYER_ID layer_all : aGerberJob->m_plotOnAllLayersSequence )
1378 {
1379 // Don't plot the same layer more than once;
1380 if( find( plotSequence.begin(), plotSequence.end(), layer_all ) != plotSequence.end() )
1381 continue;
1382
1383 plotSequence.push_back( layer_all );
1384 }
1385
1386 // Pick the basename from the board file
1387 wxFileName fn( brd->GetFileName() );
1388 wxString layerName = brd->GetLayerName( layer );
1389 wxString sheetName;
1390 wxString sheetPath;
1391 PCB_PLOT_PARAMS plotOpts;
1392
1393 if( aGerberJob->m_useBoardPlotParams )
1394 plotOpts = boardPlotOptions;
1395 else
1396 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aGerberJob, *m_reporter );
1397
1398 if( plotOpts.GetUseGerberProtelExtensions() )
1399 fileExt = GetGerberProtelExtension( layer );
1400 else
1402
1403 BuildPlotFileName( &fn, outPath, layerName, fileExt );
1404 wxString fullname = fn.GetFullName();
1405
1406 if( m_progressReporter )
1407 {
1408 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), fullname ) );
1409 m_progressReporter->KeepRefreshing();
1410 }
1411
1412 jobfile_writer.AddGbrFile( layer, fullname );
1413
1414 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1415 layerName = aJob->GetVarOverrides().at( wxT( "LAYER" ) );
1416
1417 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1418 sheetName = aJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1419
1420 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1421 sheetPath = aJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1422
1423 // We are feeding it one layer at the start here to silence a logic check
1424 GERBER_PLOTTER* plotter;
1425 plotter = (GERBER_PLOTTER*) StartPlotBoard( brd, &plotOpts, layer, layerName,
1426 fn.GetFullPath(), sheetName, sheetPath );
1427
1428 if( plotter )
1429 {
1430 m_reporter->Report( wxString::Format( _( "Plotted to '%s'.\n" ), fn.GetFullPath() ),
1432
1433 PlotBoardLayers( brd, plotter, plotSequence, plotOpts );
1434 plotter->EndPlot();
1435 }
1436 else
1437 {
1438 m_reporter->Report( wxString::Format( _( "Failed to plot to '%s'.\n" ), fn.GetFullPath() ),
1441 }
1442
1443 delete plotter;
1444 }
1445
1446 if( aGerberJob->m_createJobsFile )
1447 {
1448 wxFileName fn( brd->GetFileName() );
1449
1450 // Build gerber job file from basename
1451 BuildPlotFileName( &fn, outPath, wxT( "job" ), FILEEXT::GerberJobFileExtension );
1452 jobfile_writer.CreateJobFile( fn.GetFullPath() );
1453 }
1454
1455 return exitCode;
1456}
1457
1458
1460{
1461 JOB_EXPORT_PCB_GENCAD* aGencadJob = dynamic_cast<JOB_EXPORT_PCB_GENCAD*>( aJob );
1462
1463 if( aGencadJob == nullptr )
1465
1466 BOARD* brd = getBoard( aGencadJob->m_filename );
1467
1468 if( !brd )
1470
1471 GENCAD_EXPORTER exporter( brd );
1472
1473 VECTOR2I GencadOffset;
1474 VECTOR2I auxOrigin = brd->GetDesignSettings().GetAuxOrigin();
1475 GencadOffset.x = aGencadJob->m_useDrillOrigin ? auxOrigin.x : 0;
1476 GencadOffset.y = aGencadJob->m_useDrillOrigin ? auxOrigin.y : 0;
1477
1478 exporter.FlipBottomPads( aGencadJob->m_flipBottomPads );
1479 exporter.UsePinNamesUnique( aGencadJob->m_useUniquePins );
1480 exporter.UseIndividualShapes( aGencadJob->m_useIndividualShapes );
1481 exporter.SetPlotOffet( GencadOffset );
1482 exporter.StoreOriginCoordsInFile( aGencadJob->m_storeOriginCoords );
1483
1484 if( aGencadJob->GetConfiguredOutputPath().IsEmpty() )
1485 {
1486 wxFileName fn = brd->GetFileName();
1487 fn.SetName( fn.GetName() );
1488 fn.SetExt( FILEEXT::GencadFileExtension );
1489
1490 aGencadJob->SetWorkingOutputPath( fn.GetFullName() );
1491 }
1492
1493 wxString outPath = resolveJobOutputPath( aJob, brd );
1494
1495 if( !PATHS::EnsurePathExists( outPath, true ) )
1496 {
1497 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1499 }
1500
1501 if( !exporter.WriteFile( outPath ) )
1502 {
1503 m_reporter->Report( wxString::Format( _( "Failed to create file '%s'.\n" ), outPath ),
1505
1507 }
1508
1509 m_reporter->Report( _( "Successfully created genCAD file\n" ), RPT_SEVERITY_INFO );
1510
1511 return CLI::EXIT_CODES::OK;
1512}
1513
1514
1516{
1517 JOB_EXPORT_PCB_STATS* statsJob = dynamic_cast<JOB_EXPORT_PCB_STATS*>( aJob );
1518
1519 if( statsJob == nullptr )
1521
1522 BOARD* brd = getBoard( statsJob->m_filename );
1523
1524 if( !brd )
1526
1529
1534
1535 ComputeBoardStatistics( brd, options, data );
1536
1537 wxString projectName;
1538
1539 if( brd->GetProject() )
1540 projectName = brd->GetProject()->GetProjectName();
1541
1542 wxFileName boardFile = brd->GetFileName();
1543
1544 if( boardFile.GetName().IsEmpty() )
1545 boardFile = wxFileName( statsJob->m_filename );
1546
1548 UNITS_PROVIDER unitsProvider( pcbIUScale, unitsForReport );
1549
1550 wxString report;
1551
1553 report = FormatBoardStatisticsJson( data, brd, unitsProvider, projectName, boardFile.GetName() );
1554 else
1555 report = FormatBoardStatisticsReport( data, brd, unitsProvider, projectName, boardFile.GetName() );
1556
1557 if( statsJob->GetConfiguredOutputPath().IsEmpty() && statsJob->GetWorkingOutputPath().IsEmpty() )
1558 statsJob->SetDefaultOutputPath( boardFile.GetFullPath() );
1559
1560 wxString outPath = resolveJobOutputPath( aJob, brd );
1561
1562 if( !PATHS::EnsurePathExists( outPath, true ) )
1563 {
1564 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1566 }
1567
1568 FILE* outFile = wxFopen( outPath, wxS( "wt" ) );
1569
1570 if( !outFile )
1571 {
1572 m_reporter->Report( wxString::Format( _( "Failed to create file '%s'.\n" ), outPath ), RPT_SEVERITY_ERROR );
1574 }
1575
1576 if( fprintf( outFile, "%s", TO_UTF8( report ) ) < 0 )
1577 {
1578 fclose( outFile );
1579 m_reporter->Report( wxString::Format( _( "Error writing file '%s'.\n" ), outPath ), RPT_SEVERITY_ERROR );
1581 }
1582
1583 fclose( outFile );
1584
1585 m_reporter->Report( wxString::Format( _( "Wrote board statistics to '%s'.\n" ), outPath ), RPT_SEVERITY_ACTION );
1586
1587 statsJob->AddOutput( outPath );
1588
1589 return CLI::EXIT_CODES::OK;
1590}
1591
1592
1594{
1595 int exitCode = CLI::EXIT_CODES::OK;
1596 JOB_EXPORT_PCB_GERBER* aGerberJob = dynamic_cast<JOB_EXPORT_PCB_GERBER*>( aJob );
1597
1598 if( aGerberJob == nullptr )
1600
1601 BOARD* brd = getBoard( aGerberJob->m_filename );
1602
1603 if( !brd )
1605
1606 if( !aGerberJob->m_variant.IsEmpty() )
1607 brd->SetCurrentVariant( aGerberJob->m_variant );
1608
1609 TOOL_MANAGER* toolManager = getToolManager( brd );
1610
1611 if( aGerberJob->m_argLayers )
1612 aGerberJob->m_plotLayerSequence = convertLayerArg( aGerberJob->m_argLayers.value(), brd );
1613
1614 if( aGerberJob->m_argCommonLayers )
1615 aGerberJob->m_plotOnAllLayersSequence = convertLayerArg( aGerberJob->m_argCommonLayers.value(), brd );
1616
1617 if( aGerberJob->m_plotLayerSequence.size() < 1 )
1618 {
1619 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1621 }
1622
1623 if( aGerberJob->GetConfiguredOutputPath().IsEmpty() )
1624 {
1625 wxFileName fn = brd->GetFileName();
1626 fn.SetName( fn.GetName() );
1628
1629 aGerberJob->SetWorkingOutputPath( fn.GetFullName() );
1630 }
1631
1632 wxString outPath = resolveJobOutputPath( aJob, brd );
1633
1634 if( aGerberJob->m_checkZonesBeforePlot )
1635 {
1636 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1637 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1638
1639 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1640 }
1641
1642 PCB_PLOT_PARAMS plotOpts;
1643 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aGerberJob, *m_reporter );
1644 plotOpts.SetLayerSelection( aGerberJob->m_plotLayerSequence );
1646
1648 wxString layerName;
1649 wxString sheetName;
1650 wxString sheetPath;
1651
1652 // The first layer will be treated as the layer name for the gerber header,
1653 // the other layers will be treated equivalent to the "Plot on All Layers" option
1654 // in the GUI
1655 if( aGerberJob->m_plotLayerSequence.size() >= 1 )
1656 {
1657 layer = aGerberJob->m_plotLayerSequence.front();
1658 layerName = brd->GetLayerName( layer );
1659 }
1660
1661 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1662 layerName = aJob->GetVarOverrides().at( wxT( "LAYER" ) );
1663
1664 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1665 sheetName = aJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1666
1667 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1668 sheetPath = aJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1669
1670 // We are feeding it one layer at the start here to silence a logic check
1671 PLOTTER* plotter = StartPlotBoard( brd, &plotOpts, layer, layerName, outPath, sheetName,
1672 sheetPath );
1673
1674 if( plotter )
1675 {
1676 PlotBoardLayers( brd, plotter, aGerberJob->m_plotLayerSequence, plotOpts );
1677 plotter->EndPlot();
1678 }
1679 else
1680 {
1681 m_reporter->Report( wxString::Format( _( "Failed to plot to '%s'.\n" ), outPath ),
1684 }
1685
1686 delete plotter;
1687
1688 return exitCode;
1689}
1690
1693
1694
1696{
1697 JOB_EXPORT_PCB_DRILL* aDrillJob = dynamic_cast<JOB_EXPORT_PCB_DRILL*>( aJob );
1698
1699 if( aDrillJob == nullptr )
1701
1702 BOARD* brd = getBoard( aDrillJob->m_filename );
1703
1704 if( !brd )
1706
1707 wxString outPath = resolveJobOutputPath( aJob, brd );
1708
1709 if( !PATHS::EnsurePathExists( outPath ) )
1710 {
1711 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1713 }
1714
1715 std::unique_ptr<GENDRILL_WRITER_BASE> drillWriter;
1716
1718 drillWriter = std::make_unique<EXCELLON_WRITER>( brd );
1719 else
1720 drillWriter = std::make_unique<GERBER_WRITER>( brd );
1721
1722 VECTOR2I offset;
1723
1725 offset = VECTOR2I( 0, 0 );
1726 else
1727 offset = brd->GetDesignSettings().GetAuxOrigin();
1728
1729 PLOT_FORMAT mapFormat = PLOT_FORMAT::PDF;
1730
1731 switch( aDrillJob->m_mapFormat )
1732 {
1737 default:
1739 }
1740
1741
1742 if( aDrillJob->m_generateReport && aDrillJob->m_reportPath.IsEmpty() )
1743 {
1744 wxFileName fn = outPath;
1745 fn.SetFullName( brd->GetFileName() );
1746 fn.SetName( fn.GetName() + "-drill" );
1747 fn.SetExt( FILEEXT::ReportFileExtension );
1748
1749 aDrillJob->m_reportPath = fn.GetFullPath();
1750 }
1751
1753 {
1755
1756 switch( aDrillJob->m_zeroFormat )
1757 {
1760 break;
1761
1764 break;
1765
1768 break;
1769
1771 default:
1773 break;
1774 }
1775
1776 DRILL_PRECISION precision;
1777
1779 precision = precisionListForInches;
1780 else
1781 precision = precisionListForMetric;
1782
1783 EXCELLON_WRITER* excellonWriter = dynamic_cast<EXCELLON_WRITER*>( drillWriter.get() );
1784
1785 if( excellonWriter == nullptr )
1787
1788 excellonWriter->SetFormat( aDrillJob->m_drillUnits == JOB_EXPORT_PCB_DRILL::DRILL_UNITS::MM,
1789 zeroFmt, precision.m_Lhs, precision.m_Rhs );
1790 excellonWriter->SetOptions( aDrillJob->m_excellonMirrorY,
1791 aDrillJob->m_excellonMinimalHeader,
1792 offset, aDrillJob->m_excellonCombinePTHNPTH );
1793 excellonWriter->SetRouteModeForOvalHoles( aDrillJob->m_excellonOvalDrillRoute );
1794 excellonWriter->SetMapFileFormat( mapFormat );
1795
1796 if( !excellonWriter->CreateDrillandMapFilesSet( outPath, true, aDrillJob->m_generateMap,
1797 m_reporter ) )
1798 {
1800 }
1801
1802 if( aDrillJob->m_generateReport )
1803 {
1804 wxString reportPath = aDrillJob->ResolveOutputPath( aDrillJob->m_reportPath, true, brd->GetProject() );
1805
1806 if( !excellonWriter->GenDrillReportFile( reportPath ) )
1807 {
1809 }
1810 }
1811 }
1813 {
1814 GERBER_WRITER* gerberWriter = dynamic_cast<GERBER_WRITER*>( drillWriter.get() );
1815
1816 if( gerberWriter == nullptr )
1818
1819 // Set gerber precision: only 5 or 6 digits for mantissa are allowed
1820 // (SetFormat() accept 5 or 6, and any other value set the precision to 5)
1821 // the integer part precision is always 4, and units always mm
1822 gerberWriter->SetFormat( aDrillJob->m_gerberPrecision );
1823 gerberWriter->SetOptions( offset );
1824 gerberWriter->SetMapFileFormat( mapFormat );
1825
1826 if( !gerberWriter->CreateDrillandMapFilesSet( outPath, true, aDrillJob->m_generateMap,
1827 aDrillJob->m_generateTenting, m_reporter ) )
1828 {
1830 }
1831
1832 if( aDrillJob->m_generateReport )
1833 {
1834 wxString reportPath = aDrillJob->ResolveOutputPath( aDrillJob->m_reportPath, true, brd->GetProject() );
1835
1836 if( !gerberWriter->GenDrillReportFile( reportPath ) )
1837 {
1839 }
1840 }
1841 }
1842
1843 return CLI::EXIT_CODES::OK;
1844}
1845
1846
1848{
1849 JOB_EXPORT_PCB_POS* aPosJob = dynamic_cast<JOB_EXPORT_PCB_POS*>( aJob );
1850
1851 if( aPosJob == nullptr )
1853
1854 BOARD* brd = getBoard( aPosJob->m_filename );
1855
1856 if( !brd )
1858
1859 if( aPosJob->GetConfiguredOutputPath().IsEmpty() )
1860 {
1861 wxFileName fn = brd->GetFileName();
1862 fn.SetName( fn.GetName() );
1863
1866 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV )
1867 fn.SetExt( FILEEXT::CsvFileExtension );
1868 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::GERBER )
1869 fn.SetExt( FILEEXT::GerberFileExtension );
1870
1871 aPosJob->SetWorkingOutputPath( fn.GetFullName() );
1872 }
1873
1874 wxString outPath = resolveJobOutputPath( aJob, brd );
1875
1876 if( !PATHS::EnsurePathExists( outPath, true ) )
1877 {
1878 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1880 }
1881
1884 {
1885 wxFileName fn( outPath );
1886 wxString baseName = fn.GetName();
1887
1888 auto exportPlaceFile =
1889 [&]( bool frontSide, bool backSide, const wxString& curr_outPath ) -> bool
1890 {
1891 FILE* file = wxFopen( curr_outPath, wxS( "wt" ) );
1892 wxCHECK( file, false );
1893
1894 PLACE_FILE_EXPORTER exporter( brd,
1896 aPosJob->m_smdOnly,
1898 aPosJob->m_excludeDNP,
1899 aPosJob->m_excludeBOM,
1900 frontSide,
1901 backSide,
1904 aPosJob->m_negateBottomX );
1905
1906 // Set variant for variant-aware DNP/BOM/position file filtering
1907 exporter.SetVariant( aPosJob->m_variant );
1908
1909 std::string data = exporter.GenPositionData();
1910 fputs( data.c_str(), file );
1911 fclose( file );
1912
1913 return true;
1914 };
1915
1916 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH && !aPosJob->m_singleFile )
1917 {
1918 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( baseName, true, false ) );
1919
1920 if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV && !aPosJob->m_nakedFilename )
1921 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
1922
1923 if( exportPlaceFile( true, false, fn.GetFullPath() ) )
1924 {
1925 m_reporter->Report( wxString::Format( _( "Wrote front position data to '%s'.\n" ),
1926 fn.GetFullPath() ),
1928
1929 aPosJob->AddOutput( fn.GetFullPath() );
1930 }
1931 else
1932 {
1934 }
1935
1936 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( baseName, false, true ) );
1937
1938 if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV && !aPosJob->m_nakedFilename )
1939 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
1940
1941 if( exportPlaceFile( false, true, fn.GetFullPath() ) )
1942 {
1943 m_reporter->Report( wxString::Format( _( "Wrote back position data to '%s'.\n" ),
1944 fn.GetFullPath() ),
1946
1947 aPosJob->AddOutput( fn.GetFullPath() );
1948 }
1949 else
1950 {
1952 }
1953 }
1954 else
1955 {
1956 bool front = aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::FRONT
1958
1959 bool back = aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BACK
1961
1962 if( !aPosJob->m_nakedFilename )
1963 {
1964 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( fn.GetName(), front, back ) );
1965
1967 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
1968 }
1969
1970 if( exportPlaceFile( front, back, fn.GetFullPath() ) )
1971 {
1972 m_reporter->Report( wxString::Format( _( "Wrote position data to '%s'.\n" ),
1973 fn.GetFullPath() ),
1975
1976 aPosJob->AddOutput( fn.GetFullPath() );
1977 }
1978 else
1979 {
1981 }
1982 }
1983 }
1984 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::GERBER )
1985 {
1986 PLACEFILE_GERBER_WRITER exporter( brd );
1987
1988 // Set variant for variant-aware DNP/BOM/position file filtering
1989 exporter.SetVariant( aPosJob->m_variant );
1990
1991 PCB_LAYER_ID gbrLayer = F_Cu;
1992 wxString outPath_base = outPath;
1993
1995 || aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH )
1996 {
1997 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH || !aPosJob->m_nakedFilename )
1998 outPath = exporter.GetPlaceFileName( outPath, gbrLayer );
1999
2000 if( exporter.CreatePlaceFile( outPath, gbrLayer, aPosJob->m_gerberBoardEdge,
2001 aPosJob->m_excludeDNP, aPosJob->m_excludeBOM ) >= 0 )
2002 {
2003 m_reporter->Report( wxString::Format( _( "Wrote front position data to '%s'.\n" ), outPath ),
2005
2006 aPosJob->AddOutput( outPath );
2007 }
2008 else
2009 {
2011 }
2012 }
2013
2015 || aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH )
2016 {
2017 gbrLayer = B_Cu;
2018
2019 outPath = outPath_base;
2020
2021 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH || !aPosJob->m_nakedFilename )
2022 outPath = exporter.GetPlaceFileName( outPath, gbrLayer );
2023
2024 if( exporter.CreatePlaceFile( outPath, gbrLayer, aPosJob->m_gerberBoardEdge,
2025 aPosJob->m_excludeDNP, aPosJob->m_excludeBOM ) >= 0 )
2026 {
2027 m_reporter->Report( wxString::Format( _( "Wrote back position data to '%s'.\n" ), outPath ),
2029
2030 aPosJob->AddOutput( outPath );
2031 }
2032 else
2033 {
2035 }
2036 }
2037 }
2038
2039 return CLI::EXIT_CODES::OK;
2040}
2041
2042
2044{
2045 JOB_FP_UPGRADE* upgradeJob = dynamic_cast<JOB_FP_UPGRADE*>( aJob );
2046
2047 if( upgradeJob == nullptr )
2049
2051
2052 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
2053 {
2054 if( wxFile::Exists( upgradeJob->m_outputLibraryPath )
2055 || wxDir::Exists( upgradeJob->m_outputLibraryPath) )
2056 {
2057 m_reporter->Report( _( "Output path must not conflict with existing path\n" ),
2060 }
2061 }
2062 else if( fileType != PCB_IO_MGR::KICAD_SEXP )
2063 {
2064 m_reporter->Report( _( "Output path must be specified to convert legacy and non-KiCad libraries\n" ),
2066
2068 }
2069
2071 {
2072 if( !wxDir::Exists( upgradeJob->m_libraryPath ) )
2073 {
2074 m_reporter->Report( _( "Footprint library path does not exist or is not accessible\n" ),
2077 }
2078
2080 FP_CACHE fpLib( &pcb_io, upgradeJob->m_libraryPath );
2081
2082 try
2083 {
2084 fpLib.Load();
2085 }
2086 catch( ... )
2087 {
2088 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
2090 }
2091
2092 if( m_progressReporter )
2093 m_progressReporter->KeepRefreshing();
2094
2095 bool shouldSave = upgradeJob->m_force;
2096
2097 for( const auto& footprint : fpLib.GetFootprints() )
2098 {
2099 if( footprint.second->GetFootprint()->GetFileFormatVersionAtLoad() < SEXPR_BOARD_FILE_VERSION )
2100 shouldSave = true;
2101 }
2102
2103 if( shouldSave )
2104 {
2105 try
2106 {
2107 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
2108 fpLib.SetPath( upgradeJob->m_outputLibraryPath );
2109
2110 fpLib.Save();
2111 }
2112 catch( ... )
2113 {
2114 m_reporter->Report( _( "Unable to save library\n" ), RPT_SEVERITY_ERROR );
2116 }
2117 }
2118 else
2119 {
2120 m_reporter->Report( _( "Footprint library was not updated\n" ), RPT_SEVERITY_ERROR );
2121 }
2122 }
2123 else
2124 {
2125 if( !PCB_IO_MGR::ConvertLibrary( {}, upgradeJob->m_libraryPath,
2126 upgradeJob->m_outputLibraryPath, nullptr /* REPORTER */ ) )
2127 {
2128 m_reporter->Report( ( "Unable to convert library\n" ), RPT_SEVERITY_ERROR );
2130 }
2131 }
2132
2133 return CLI::EXIT_CODES::OK;
2134}
2135
2136
2138{
2139 JOB_FP_EXPORT_SVG* svgJob = dynamic_cast<JOB_FP_EXPORT_SVG*>( aJob );
2140
2141 if( svgJob == nullptr )
2143
2145 FP_CACHE fpLib( &pcb_io, svgJob->m_libraryPath );
2146
2147 if( svgJob->m_argLayers )
2148 {
2149 if( !svgJob->m_argLayers.value().empty() )
2150 svgJob->m_plotLayerSequence = convertLayerArg( svgJob->m_argLayers.value(), nullptr );
2151 else
2153 }
2154
2155 try
2156 {
2157 fpLib.Load();
2158 }
2159 catch( ... )
2160 {
2161 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
2163 }
2164
2165 wxString outPath = svgJob->GetFullOutputPath( nullptr );
2166
2167 if( !PATHS::EnsurePathExists( outPath, true ) )
2168 {
2169 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2171 }
2172
2173 int exitCode = CLI::EXIT_CODES::OK;
2174 bool singleFpPlotted = false;
2175
2176 for( const auto& [fpName, fpCacheEntry] : fpLib.GetFootprints() )
2177 {
2178 if( m_progressReporter )
2179 {
2180 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), fpName ) );
2181 m_progressReporter->KeepRefreshing();
2182 }
2183
2184 if( !svgJob->m_footprint.IsEmpty() )
2185 {
2186 // skip until we find the right footprint
2187 if( fpName != svgJob->m_footprint )
2188 continue;
2189 else
2190 singleFpPlotted = true;
2191 }
2192
2193 exitCode = doFpExportSvg( svgJob, fpCacheEntry->GetFootprint().get() );
2194
2195 if( exitCode != CLI::EXIT_CODES::OK )
2196 break;
2197 }
2198
2199 if( !svgJob->m_footprint.IsEmpty() && !singleFpPlotted )
2200 {
2201 m_reporter->Report( _( "The given footprint could not be found to export." ) + wxS( "\n" ),
2203 }
2204
2205 return CLI::EXIT_CODES::OK;
2206}
2207
2208
2210{
2211 // the hack for now is we create fake boards containing the footprint and plot the board
2212 // until we refactor better plot api later
2213 std::unique_ptr<BOARD> brd;
2214 brd.reset( CreateEmptyBoard() );
2215 brd->GetProject()->ApplyTextVars( aSvgJob->GetVarOverrides() );
2216 brd->SynchronizeProperties();
2217
2218 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aFootprint->Clone() );
2219
2220 if( fp == nullptr )
2222
2223 fp->SetLink( niluuid );
2224 fp->SetFlags( IS_NEW );
2225 fp->SetParent( brd.get() );
2226
2227 for( PAD* pad : fp->Pads() )
2228 {
2229 pad->SetLocalRatsnestVisible( false );
2230 pad->SetNetCode( 0 );
2231 }
2232
2233 fp->SetOrientation( ANGLE_0 );
2234 fp->SetPosition( VECTOR2I( 0, 0 ) );
2235
2236 brd->Add( fp, ADD_MODE::INSERT, true );
2237
2238 wxFileName outputFile;
2239 outputFile.SetPath( aSvgJob->GetFullOutputPath(nullptr) );
2240 outputFile.SetName( aFootprint->GetFPID().GetLibItemName().wx_str() );
2241 outputFile.SetExt( FILEEXT::SVGFileExtension );
2242
2243 m_reporter->Report( wxString::Format( _( "Plotting footprint '%s' to '%s'\n" ),
2244 aFootprint->GetFPID().GetLibItemName().wx_str(),
2245 outputFile.GetFullPath() ),
2247
2248 PCB_PLOT_PARAMS plotOpts;
2249 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aSvgJob, *m_reporter );
2250
2251 // always fixed for the svg plot
2252 plotOpts.SetPlotFrameRef( false );
2253 plotOpts.SetSvgFitPageToBoard( true );
2254 plotOpts.SetMirror( false );
2255 plotOpts.SetSkipPlotNPTH_Pads( false );
2256
2257 if( plotOpts.GetSketchPadsOnFabLayers() )
2258 {
2259 plotOpts.SetPlotPadNumbers( true );
2260 }
2261
2262 PCB_PLOTTER plotter( brd.get(), m_reporter, plotOpts );
2263
2264 if( !plotter.Plot( outputFile.GetFullPath(),
2265 aSvgJob->m_plotLayerSequence,
2267 false,
2268 true,
2269 wxEmptyString, wxEmptyString,
2270 wxEmptyString ) )
2271 {
2272 m_reporter->Report( _( "Error creating svg file" ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
2274 }
2275
2276 return CLI::EXIT_CODES::OK;
2277}
2278
2279
2281{
2282 JOB_PCB_DRC* drcJob = dynamic_cast<JOB_PCB_DRC*>( aJob );
2283
2284 if( drcJob == nullptr )
2286
2287 BOARD* brd = getBoard( drcJob->m_filename );
2288
2289 if( !brd )
2291
2292 // Running DRC requires libraries be loaded, so make sure they have been
2294 adapter->AsyncLoad();
2295 adapter->BlockUntilLoaded();
2296
2297 if( drcJob->GetConfiguredOutputPath().IsEmpty() )
2298 {
2299 wxFileName fn = brd->GetFileName();
2300 fn.SetName( fn.GetName() + wxS( "-drc" ) );
2301
2303 fn.SetExt( FILEEXT::JsonFileExtension );
2304 else
2305 fn.SetExt( FILEEXT::ReportFileExtension );
2306
2307 drcJob->SetWorkingOutputPath( fn.GetFullName() );
2308 }
2309
2310 wxString outPath = resolveJobOutputPath( aJob, brd );
2311
2312 if( !PATHS::EnsurePathExists( outPath, true ) )
2313 {
2314 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2316 }
2317
2318 EDA_UNITS units;
2319
2320 switch( drcJob->m_units )
2321 {
2322 case JOB_PCB_DRC::UNITS::INCH: units = EDA_UNITS::INCH; break;
2323 case JOB_PCB_DRC::UNITS::MILS: units = EDA_UNITS::MILS; break;
2324 case JOB_PCB_DRC::UNITS::MM: units = EDA_UNITS::MM; break;
2325 default: units = EDA_UNITS::MM; break;
2326 }
2327
2328 std::shared_ptr<DRC_ENGINE> drcEngine = brd->GetDesignSettings().m_DRCEngine;
2329 std::unique_ptr<NETLIST> netlist = std::make_unique<NETLIST>();
2330
2331 drcEngine->SetDrawingSheet( getDrawingSheetProxyView( brd ) );
2332
2333 // BOARD_COMMIT uses TOOL_MANAGER to grab the board internally so we must give it one
2334 TOOL_MANAGER* toolManager = getToolManager( brd );
2335
2336 BOARD_COMMIT commit( toolManager );
2337 bool checkParity = drcJob->m_parity;
2338 std::string netlist_str;
2339
2340 if( checkParity )
2341 {
2342 wxString annotateMsg = _( "Schematic parity tests require a fully annotated schematic." );
2343 netlist_str = annotateMsg;
2344
2345 // The KIFACE_NETLIST_SCHEMATIC function has some broken-ness that the schematic
2346 // frame's version does not, but it is the only one that works in CLI, so we use it
2347 // if we don't have the sch frame open.
2348 // TODO: clean this up, see https://gitlab.com/kicad/code/kicad/-/issues/19929
2349 if( m_kiway->Player( FRAME_SCH, false ) )
2350 {
2351 m_kiway->ExpressMail( FRAME_SCH, MAIL_SCH_GET_NETLIST, netlist_str );
2352 }
2353 else
2354 {
2355 wxFileName schematicPath( drcJob->m_filename );
2356 schematicPath.SetExt( FILEEXT::KiCadSchematicFileExtension );
2357
2358 if( !schematicPath.Exists() )
2359 schematicPath.SetExt( FILEEXT::LegacySchematicFileExtension );
2360
2361 if( !schematicPath.Exists() )
2362 {
2363 m_reporter->Report( _( "Failed to fetch schematic netlist for parity tests.\n" ),
2365 checkParity = false;
2366 }
2367 else
2368 {
2369 typedef bool ( *NETLIST_FN_PTR )( const wxString&, std::string& );
2370 KIFACE* eeschema = m_kiway->KiFACE( KIWAY::FACE_SCH );
2371 NETLIST_FN_PTR netlister =
2372 (NETLIST_FN_PTR) eeschema->IfaceOrAddress( KIFACE_NETLIST_SCHEMATIC );
2373 ( *netlister )( schematicPath.GetFullPath(), netlist_str );
2374 }
2375 }
2376
2377 if( netlist_str == annotateMsg )
2378 {
2379 m_reporter->Report( wxString( netlist_str ) + wxT( "\n" ), RPT_SEVERITY_ERROR );
2380 checkParity = false;
2381 }
2382 }
2383
2384 if( checkParity )
2385 {
2386 try
2387 {
2388 STRING_LINE_READER* lineReader = new STRING_LINE_READER( netlist_str,
2389 _( "Eeschema netlist" ) );
2390 KICAD_NETLIST_READER netlistReader( lineReader, netlist.get() );
2391
2392 netlistReader.LoadNetlist();
2393 }
2394 catch( const IO_ERROR& )
2395 {
2396 m_reporter->Report( _( "Failed to fetch schematic netlist for parity tests.\n" ),
2398 checkParity = false;
2399 }
2400
2401 drcEngine->SetSchematicNetlist( netlist.get() );
2402 }
2403
2404 if( drcJob->m_refillZones )
2405 {
2406 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
2407 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
2408
2409 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
2410 }
2411
2412 drcEngine->SetProgressReporter( m_progressReporter );
2413 drcEngine->SetViolationHandler(
2414 [&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos, int aLayer,
2415 const std::function<void( PCB_MARKER* )>& aPathGenerator )
2416 {
2417 PCB_MARKER* marker = new PCB_MARKER( aItem, aPos, aLayer );
2418 aPathGenerator( marker );
2419 commit.Add( marker );
2420 } );
2421
2422 brd->RecordDRCExclusions();
2423 brd->DeleteMARKERs( true, true );
2424 drcEngine->RunTests( units, drcJob->m_reportAllTrackErrors, checkParity );
2425 drcEngine->ClearViolationHandler();
2426
2427 commit.Push( _( "DRC" ), SKIP_UNDO | SKIP_SET_DIRTY );
2428
2429 // Update the exclusion status on any excluded markers that still exist.
2430 brd->ResolveDRCExclusions( false );
2431
2432 std::shared_ptr<DRC_ITEMS_PROVIDER> markersProvider = std::make_shared<DRC_ITEMS_PROVIDER>(
2434
2435 std::shared_ptr<DRC_ITEMS_PROVIDER> ratsnestProvider =
2436 std::make_shared<DRC_ITEMS_PROVIDER>( brd, MARKER_BASE::MARKER_RATSNEST );
2437
2438 std::shared_ptr<DRC_ITEMS_PROVIDER> fpWarningsProvider =
2439 std::make_shared<DRC_ITEMS_PROVIDER>( brd, MARKER_BASE::MARKER_PARITY );
2440
2441 markersProvider->SetSeverities( drcJob->m_severity );
2442 ratsnestProvider->SetSeverities( drcJob->m_severity );
2443 fpWarningsProvider->SetSeverities( drcJob->m_severity );
2444
2445 m_reporter->Report( wxString::Format( _( "Found %d violations\n" ),
2446 markersProvider->GetCount() ),
2448 m_reporter->Report( wxString::Format( _( "Found %d unconnected items\n" ),
2449 ratsnestProvider->GetCount() ),
2451
2452 if( checkParity )
2453 {
2454 m_reporter->Report( wxString::Format( _( "Found %d schematic parity issues\n" ),
2455 fpWarningsProvider->GetCount() ),
2457 }
2458
2459 DRC_REPORT reportWriter( brd, units, markersProvider, ratsnestProvider, fpWarningsProvider );
2460
2461 bool wroteReport = false;
2462
2464 wroteReport = reportWriter.WriteJsonReport( outPath );
2465 else
2466 wroteReport = reportWriter.WriteTextReport( outPath );
2467
2468 if( !wroteReport )
2469 {
2470 m_reporter->Report( wxString::Format( _( "Unable to save DRC report to %s\n" ), outPath ),
2473 }
2474
2475 m_reporter->Report( wxString::Format( _( "Saved DRC Report to %s\n" ), outPath ),
2477
2478 if( drcJob->m_refillZones && drcJob->m_saveBoard )
2479 {
2480 if( SaveBoard( drcJob->m_filename, brd, true ) )
2481 {
2482 m_reporter->Report( _( "Saved board\n" ), RPT_SEVERITY_ACTION );
2483 }
2484 else
2485 {
2486 m_reporter->Report( _( "Failed to save board.\n" ), RPT_SEVERITY_ERROR );
2487
2489 }
2490 }
2491
2492 if( drcJob->m_exitCodeViolations )
2493 {
2494 if( markersProvider->GetCount() > 0 || ratsnestProvider->GetCount() > 0
2495 || fpWarningsProvider->GetCount() > 0 )
2496 {
2498 }
2499 }
2500
2502}
2503
2504
2506{
2507 JOB_EXPORT_PCB_IPC2581* job = dynamic_cast<JOB_EXPORT_PCB_IPC2581*>( aJob );
2508
2509 if( job == nullptr )
2511
2512 BOARD* brd = getBoard( job->m_filename );
2513
2514 if( !brd )
2516
2517 if( !job->m_variant.IsEmpty() )
2518 brd->SetCurrentVariant( job->m_variant );
2519
2520 if( job->GetConfiguredOutputPath().IsEmpty() )
2521 {
2522 wxFileName fn = brd->GetFileName();
2523 fn.SetName( fn.GetName() );
2524 fn.SetExt( FILEEXT::Ipc2581FileExtension );
2525
2526 job->SetWorkingOutputPath( fn.GetName() );
2527 }
2528
2529 wxString outPath = resolveJobOutputPath( aJob, brd );
2530
2531 if( !PATHS::EnsurePathExists( outPath, true ) )
2532 {
2533 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2535 }
2536
2537 std::map<std::string, UTF8> props;
2538 props["units"] = job->m_units == JOB_EXPORT_PCB_IPC2581::IPC2581_UNITS::MM ? "mm" : "inch";
2539 props["sigfig"] = wxString::Format( "%d", job->m_precision );
2540 props["version"] = job->m_version == JOB_EXPORT_PCB_IPC2581::IPC2581_VERSION::C ? "C" : "B";
2541 props["OEMRef"] = job->m_colInternalId;
2542 props["mpn"] = job->m_colMfgPn;
2543 props["mfg"] = job->m_colMfg;
2544 props["dist"] = job->m_colDist;
2545 props["distpn"] = job->m_colDistPn;
2546
2547 wxString bomRev = job->m_bomRev;
2548
2549 if( bomRev.IsEmpty() && brd->GetProject() )
2550 {
2551 const IP2581_BOM& bomSettings = brd->GetProject()->GetProjectFile().m_IP2581Bom;
2552 bomRev = bomSettings.bomRev;
2553
2554 if( bomRev.IsEmpty() )
2555 bomRev = bomSettings.schRevision;
2556 }
2557
2558 if( !bomRev.IsEmpty() )
2559 props["bomrev"] = bomRev;
2560
2561 wxString tempFile = wxFileName::CreateTempFileName( wxS( "pcbnew_ipc" ) );
2562 try
2563 {
2565 pi->SetProgressReporter( m_progressReporter );
2566 pi->SaveBoard( tempFile, brd, &props );
2567 }
2568 catch( const IO_ERROR& ioe )
2569 {
2570 m_reporter->Report( wxString::Format( _( "Error generating IPC-2581 file '%s'.\n%s" ),
2571 job->m_filename,
2572 ioe.What() ),
2574
2575 wxRemoveFile( tempFile );
2576
2578 }
2579
2580 if( job->m_compress )
2581 {
2582 wxFileName tempfn = outPath;
2583 tempfn.SetExt( FILEEXT::Ipc2581FileExtension );
2584 wxFileName zipfn = tempFile;
2585 zipfn.SetExt( "zip" );
2586
2587 {
2588 wxFFileOutputStream fnout( zipfn.GetFullPath() );
2589
2590 // Use a large I/O buffer to improve compatibility with cloud-synced folders.
2591 // See KIPLATFORM::IO::CLOUD_SYNC_BUFFER_SIZE comment for details.
2592 if( FILE* fp = fnout.GetFile()->fp() )
2593 setvbuf( fp, nullptr, _IOFBF, KIPLATFORM::IO::CLOUD_SYNC_BUFFER_SIZE );
2594
2595 wxZipOutputStream zip( fnout );
2596 wxFFileInputStream fnin( tempFile );
2597
2598 zip.PutNextEntry( tempfn.GetFullName() );
2599 fnin.Read( zip );
2600 }
2601
2602 wxRemoveFile( tempFile );
2603 tempFile = zipfn.GetFullPath();
2604 }
2605
2606 // If save succeeded, replace the original with what we just wrote
2607 if( !wxRenameFile( tempFile, outPath ) )
2608 {
2609 m_reporter->Report( wxString::Format( _( "Error generating IPC-2581 file '%s'.\n"
2610 "Failed to rename temporary file '%s." ),
2611 outPath,
2612 tempFile ),
2614 }
2615
2617}
2618
2619
2621{
2622 JOB_EXPORT_PCB_IPCD356* job = dynamic_cast<JOB_EXPORT_PCB_IPCD356*>( aJob );
2623
2624 if( job == nullptr )
2626
2627 BOARD* brd = getBoard( job->m_filename );
2628
2629 if( !brd )
2631
2632 if( job->GetConfiguredOutputPath().IsEmpty() )
2633 {
2634 wxFileName fn = brd->GetFileName();
2635 fn.SetName( fn.GetName() );
2636 fn.SetExt( FILEEXT::IpcD356FileExtension );
2637
2638 job->SetWorkingOutputPath( fn.GetFullName() );
2639 }
2640
2641 wxString outPath = resolveJobOutputPath( aJob, brd );
2642
2643 if( !PATHS::EnsurePathExists( outPath, true ) )
2644 {
2645 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2647 }
2648
2649 IPC356D_WRITER exporter( brd );
2650
2651 bool success = exporter.Write( outPath );
2652
2653 if( success )
2654 {
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 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
2722 if( m_reporter->HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
2724
2726}
2727
2729{
2730 JOB_PCB_UPGRADE* job = dynamic_cast<JOB_PCB_UPGRADE*>( aJob );
2731
2732 if( job == nullptr )
2734
2735 bool shouldSave = job->m_force;
2736
2737 try
2738 {
2740 BOARD* brd = getBoard( job->m_filename );
2742 shouldSave = true;
2743
2744 if( shouldSave )
2745 {
2746 pi->SaveBoard( brd->GetFileName(), brd );
2747 m_reporter->Report( _( "Successfully saved board file using the latest format\n" ), RPT_SEVERITY_INFO );
2748 }
2749 else
2750 {
2751 m_reporter->Report( _( "Board file was not updated\n" ), RPT_SEVERITY_ERROR );
2752 }
2753 }
2754 catch( const IO_ERROR& ioe )
2755 {
2756 wxString msg =
2757 wxString::Format( _( "Error saving board file '%s'.\n%s" ), job->m_filename, ioe.What().GetData() );
2758 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2760 }
2761
2763}
2764
2765// Most job handlers need to align the running job with the board before resolving any
2766// output paths with variables in them like ${REVISION}.
2767wxString PCBNEW_JOBS_HANDLER::resolveJobOutputPath( JOB* aJob, BOARD* aBoard, const wxString* aDrawingSheet )
2768{
2769 aJob->SetTitleBlock( aBoard->GetTitleBlock() );
2770
2771 if( aDrawingSheet && !aDrawingSheet->IsEmpty() )
2772 loadOverrideDrawingSheet( aBoard, *aDrawingSheet );
2773
2774 PROJECT* project = aBoard->GetProject();
2775
2776 if( project )
2777 project->ApplyTextVars( aJob->GetVarOverrides() );
2778
2779 aBoard->SynchronizeProperties();
2780
2781 return aJob->GetFullOutputPath( project );
2782}
2783
2784
2786{
2788 &aBrd->GetPageSettings(),
2789 aBrd->GetProject(),
2790 &aBrd->GetTitleBlock(),
2791 &aBrd->GetProperties() );
2792
2793 drawingSheet->SetSheetName( std::string() );
2794 drawingSheet->SetSheetPath( std::string() );
2795 drawingSheet->SetIsFirstPage( true );
2796
2797 drawingSheet->SetFileName( TO_UTF8( aBrd->GetFileName() ) );
2798
2799 return drawingSheet;
2800}
2801
2802
2803void PCBNEW_JOBS_HANDLER::loadOverrideDrawingSheet( BOARD* aBrd, const wxString& aSheetPath )
2804{
2805 // dont bother attempting to load a empty path, if there was one
2806 if( aSheetPath.IsEmpty() )
2807 return;
2808
2809 auto loadSheet =
2810 [&]( const wxString& path ) -> bool
2811 {
2814 resolver.SetProject( aBrd->GetProject() );
2815 resolver.SetProgramBase( &Pgm() );
2816
2817 wxString filename = resolver.ResolvePath( BASE_SCREEN::m_DrawingSheetFileName,
2818 aBrd->GetProject()->GetProjectPath(),
2819 { aBrd->GetEmbeddedFiles() } );
2820 wxString msg;
2821
2822 if( !DS_DATA_MODEL::GetTheInstance().LoadDrawingSheet( filename, &msg ) )
2823 {
2824 m_reporter->Report( wxString::Format( _( "Error loading drawing sheet '%s'." ),
2825 path )
2826 + wxS( "\n" ) + msg + wxS( "\n" ),
2828 return false;
2829 }
2830
2831 return true;
2832 };
2833
2834 if( loadSheet( aSheetPath ) )
2835 return;
2836
2837 // failed loading custom path, revert back to default
2838 loadSheet( aBrd->GetProject()->GetProjectFile().m_BoardDrawingSheetFile );
2839}
2840
2841
2843{
2844 JOB_PCB_IMPORT* job = dynamic_cast<JOB_PCB_IMPORT*>( aJob );
2845
2846 if( !job )
2848
2849 // Map job format to PCB_IO file type
2851
2852 switch( job->m_format )
2853 {
2856 break;
2857
2860 break;
2861
2864 break;
2865
2868 break;
2869
2872 break;
2873
2876 break;
2877
2880 break;
2881
2884 break;
2885 }
2886
2888 {
2889 m_reporter->Report( wxString::Format( _( "Unable to determine file format for '%s'\n" ),
2890 job->m_inputFile ),
2893 }
2894
2895 // Check that input file exists
2896 if( !wxFile::Exists( job->m_inputFile ) )
2897 {
2898 m_reporter->Report( wxString::Format( _( "Input file not found: '%s'\n" ),
2899 job->m_inputFile ),
2902 }
2903
2904 // Determine output path
2905 wxString outputPath = job->GetConfiguredOutputPath();
2906
2907 if( outputPath.IsEmpty() )
2908 {
2909 wxFileName fn( job->m_inputFile );
2910 fn.SetExt( FILEEXT::KiCadPcbFileExtension );
2911 outputPath = fn.GetFullPath();
2912 }
2913
2914 BOARD* board = nullptr;
2915 wxString formatName = PCB_IO_MGR::ShowType( fileType );
2916 std::vector<wxString> warnings;
2917
2918 try
2919 {
2921
2922 if( !pi )
2923 {
2924 m_reporter->Report( wxString::Format( _( "No plugin found for file type '%s'\n" ),
2925 formatName ),
2928 }
2929
2930 m_reporter->Report( wxString::Format( _( "Importing '%s' using %s format...\n" ),
2931 job->m_inputFile, formatName ),
2933
2934 board = pi->LoadBoard( job->m_inputFile, nullptr, nullptr, nullptr );
2935
2936 if( !board )
2937 {
2938 m_reporter->Report( _( "Failed to load board\n" ), RPT_SEVERITY_ERROR );
2940 }
2941
2942 // Save as KiCad format
2944 kicadPlugin->SaveBoard( outputPath, board );
2945
2946 m_reporter->Report( wxString::Format( _( "Successfully saved imported board to '%s'\n" ),
2947 outputPath ),
2949
2950 // Generate report if requested
2952 {
2953 wxFileName inputFn( job->m_inputFile );
2954 wxFileName outputFn( outputPath );
2955
2956 // Count board statistics
2957 size_t footprintCount = board->Footprints().size();
2958 size_t trackCount = 0;
2959 size_t viaCount = 0;
2960 size_t zoneCount = board->Zones().size();
2961
2962 for( PCB_TRACK* track : board->Tracks() )
2963 {
2964 if( track->Type() == PCB_VIA_T )
2965 viaCount++;
2966 else
2967 trackCount++;
2968 }
2969
2970 // Build layer mapping info
2971 nlohmann::json layerMappings = nlohmann::json::object();
2972 LSEQ enabledLayers = board->GetEnabledLayers().Seq();
2973
2974 for( PCB_LAYER_ID layer : enabledLayers )
2975 {
2976 wxString layerName = board->GetLayerName( layer );
2977
2978 layerMappings[layerName.ToStdString()] = {
2979 { "kicad_layer", LSET::Name( layer ).ToStdString() },
2980 { "method", "auto" }
2981 };
2982 }
2983
2985 {
2986 nlohmann::json report;
2987
2988 report["source_file"] = inputFn.GetFullName().ToStdString();
2989 report["source_format"] = formatName.ToStdString();
2990 report["output_file"] = outputFn.GetFullName().ToStdString();
2991 report["layer_mapping"] = layerMappings;
2992 report["statistics"] = {
2993 { "footprints", footprintCount },
2994 { "tracks", trackCount },
2995 { "vias", viaCount },
2996 { "zones", zoneCount }
2997 };
2998
2999 nlohmann::json warningsJson = nlohmann::json::array();
3000
3001 for( const wxString& warning : warnings )
3002 warningsJson.push_back( warning.ToStdString() );
3003
3004 report["warnings"] = warningsJson;
3005 report["errors"] = nlohmann::json::array();
3006
3007 wxString reportOutput = wxString::FromUTF8( report.dump( 2 ) );
3008
3009 if( !job->m_reportFile.IsEmpty() )
3010 {
3011 wxFile file( job->m_reportFile, wxFile::write );
3012
3013 if( file.IsOpened() )
3014 {
3015 file.Write( reportOutput );
3016 file.Close();
3017 }
3018 }
3019 else
3020 {
3021 m_reporter->Report( reportOutput + wxS( "\n" ), RPT_SEVERITY_INFO );
3022 }
3023 }
3025 {
3026 wxString text;
3027
3028 text += wxString::Format( wxS( "Import Report\n" ) );
3029 text += wxString::Format( wxS( "=============\n\n" ) );
3030 text += wxString::Format( wxS( "Source file: %s\n" ), inputFn.GetFullName() );
3031 text += wxString::Format( wxS( "Source format: %s\n" ), formatName );
3032 text += wxString::Format( wxS( "Output file: %s\n\n" ), outputFn.GetFullName() );
3033 text += wxS( "Statistics:\n" );
3034 text += wxString::Format( wxS( " Footprints: %zu\n" ), footprintCount );
3035 text += wxString::Format( wxS( " Tracks: %zu\n" ), trackCount );
3036 text += wxString::Format( wxS( " Vias: %zu\n" ), viaCount );
3037 text += wxString::Format( wxS( " Zones: %zu\n" ), zoneCount );
3038
3039 if( !warnings.empty() )
3040 {
3041 text += wxS( "\nWarnings:\n" );
3042
3043 for( const wxString& warning : warnings )
3044 text += wxString::Format( wxS( " - %s\n" ), warning );
3045 }
3046
3047 if( !job->m_reportFile.IsEmpty() )
3048 {
3049 wxFile file( job->m_reportFile, wxFile::write );
3050
3051 if( file.IsOpened() )
3052 {
3053 file.Write( text );
3054 file.Close();
3055 }
3056 }
3057 else
3058 {
3059 m_reporter->Report( text, RPT_SEVERITY_INFO );
3060 }
3061 }
3062 }
3063
3064 delete board;
3065 }
3066 catch( const IO_ERROR& ioe )
3067 {
3068 m_reporter->Report( wxString::Format( _( "Error during import: %s\n" ), ioe.What() ),
3070
3071 delete board;
3073 }
3074
3076}
@ 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
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
void SetCurrentVariant(const wxString &aVariant)
Definition board.cpp:2515
const PAGE_INFO & GetPageSettings() const
Definition board.h:799
const ZONES & Zones() const
Definition board.h:367
void RecordDRCExclusions()
Scan existing markers and record data from any that are Excluded.
Definition board.cpp:330
TITLE_BLOCK & GetTitleBlock()
Definition board.h:805
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:400
const FOOTPRINTS & Footprints() const
Definition board.h:363
const TRACKS & Tracks() const
Definition board.h:361
const wxString & GetFileName() const
Definition board.h:359
std::vector< PCB_MARKER * > ResolveDRCExclusions(bool aCreateMarkers)
Rebuild DRC markers from the serialized data in BOARD_DESIGN_SETTINGS.
Definition board.cpp:387
int GetFileFormatVersionAtLoad() const
Definition board.h:456
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition board.h:802
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:729
PROJECT * GetProject() const
Definition board.h:579
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1083
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:968
void SynchronizeProperties()
Copy the current project's text variables into the boards property cache.
Definition board.cpp:2495
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false, bool aPhysicalLayersOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition board.cpp:2117
void DeleteMARKERs()
Delete all MARKERS from the board.
Definition board.cpp:1719
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 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 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:148
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:1074
void SetOrientation(const EDA_ANGLE &aNewAngle)
EDA_ITEM * Clone() const override
Invoke a function on all children.
std::deque< PAD * > & Pads()
Definition footprint.h:306
const LIB_ID & GetFPID() const
Definition footprint.h:351
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:295
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition kiway.cpp:407
@ FACE_SCH
eeschema DSO
Definition kiway.h:302
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:515
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.
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)
static void PlotJobToPlotOpts(PCB_PLOT_PARAMS &aOpts, JOB_EXPORT_PCB_PLOT *aJob, REPORTER &aReporter)
Translate a JOB to PCB_PLOT_PARAMS.
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:131
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 DNP filtering.
Base plotter engine class.
Definition plotter.h:136
virtual bool EndPlot()=0
wxString m_BoardDrawingSheetFile
PcbNew params.
struct IP2581_BOM m_IP2581Bom
Layer pair list for the board.
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:65
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:203
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,...
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 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)
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:553
@ LAYER_3D_BACKGROUND_BOTTOM
Definition layer_ids.h:552
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
static constexpr size_t CLOUD_SYNC_BUFFER_SIZE
Buffer size for file I/O operations on cloud-synced folders.
Definition io.h:77
#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)
SETTINGS_MANAGER * GetSettingsManager()
BOARD * CreateEmptyBoard()
Construct a default BOARD with a temporary (no filename) project.
bool SaveBoard(wxString &aFileName, BOARD *aBoard, PCB_IO_MGR::PCB_FILE_T aFormat, bool aSkipSettings)
BOARD * LoadBoard(const wxString &aFileName, bool aSetActive)
Loads a board from file This function identifies the file type by extension and determines the correc...
const wxString GetGerberProtelExtension(int aLayer)
Definition pcbplot.cpp:44
void BuildPlotFileName(wxFileName *aFilename, const wxString &aOutputDir, const wxString &aSuffix, const wxString &aExtension)
Complete a plot filename.
Definition pcbplot.cpp:380
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
wxString bomRev
Explicit BOM revision override set by user.
wxString schRevision
Auto-propagated schematic title block revision.
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:97
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
Definition of file extensions used in Kicad.
glm::vec3 SFVEC3F
Definition xv3d_types.h:44
#define ZONE_FILLER_TOOL_NAME