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