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>
59#include <jobs/job_pcb_render.h>
60#include <jobs/job_pcb_drc.h>
61#include <jobs/job_pcb_import.h>
63#include <eda_units.h>
65#include <lset.h>
66#include <cli/exit_codes.h>
72#include <tool/tool_manager.h>
73#include <tools/drc_tool.h>
74#include <filename_resolver.h>
79#include <kiface_base.h>
80#include <macros.h>
81#include <pad.h>
82#include <pcb_marker.h>
86#include <kiface_ids.h>
89#include <pcbnew_settings.h>
90#include <pcbplot.h>
91#include <pcb_plotter.h>
92#include <pcb_edit_frame.h>
93#include <pcb_track.h>
94#include <pgm_base.h>
97#include <project_pcb.h>
99#include <reporter.h>
100#include <progress_reporter.h>
102#include <export_vrml.h>
103#include <kiplatform/io.h>
110#include <dialogs/dialog_plot.h>
115#include <paths.h>
117
118#include <locale_io.h>
119#include <confirm.h>
120
121
122#ifdef _WIN32
123#ifdef TRANSPARENT
124#undef TRANSPARENT
125#endif
126#endif
127
128
130 JOB_DISPATCHER( aKiway ),
131 m_cliBoard( nullptr ),
132 m_toolManager( nullptr )
133{
134 Register( "3d", std::bind( &PCBNEW_JOBS_HANDLER::JobExportStep, this, std::placeholders::_1 ),
135 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
136 {
137 JOB_EXPORT_PCB_3D* svgJob = dynamic_cast<JOB_EXPORT_PCB_3D*>( job );
138
139 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
140 false ) );
141
142 wxCHECK( svgJob && editFrame, false );
143
144 DIALOG_EXPORT_STEP dlg( editFrame, aParent, "", svgJob );
145 return dlg.ShowModal() == wxID_OK;
146 } );
147 Register( "render",
148 std::bind( &PCBNEW_JOBS_HANDLER::JobExportRender, this, std::placeholders::_1 ),
149 []( JOB* job, wxWindow* aParent ) -> bool
150 {
151 JOB_PCB_RENDER* renderJob = dynamic_cast<JOB_PCB_RENDER*>( job );
152
153 wxCHECK( renderJob, false );
154
155 DIALOG_RENDER_JOB dlg( aParent, renderJob );
156 return dlg.ShowModal() == wxID_OK;
157 } );
158 Register( "upgrade", std::bind( &PCBNEW_JOBS_HANDLER::JobUpgrade, this, std::placeholders::_1 ),
159 []( JOB* job, wxWindow* aParent ) -> bool
160 {
161 return true;
162 } );
163 Register( "pcb_import", std::bind( &PCBNEW_JOBS_HANDLER::JobImport, this, std::placeholders::_1 ),
164 []( JOB* job, wxWindow* aParent ) -> bool
165 {
166 return true;
167 } );
168 Register( "svg", std::bind( &PCBNEW_JOBS_HANDLER::JobExportSvg, this, std::placeholders::_1 ),
169 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
170 {
171 JOB_EXPORT_PCB_SVG* svgJob = dynamic_cast<JOB_EXPORT_PCB_SVG*>( job );
172
173 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
174 false ) );
175
176 wxCHECK( svgJob && editFrame, false );
177
178 DIALOG_PLOT dlg( editFrame, aParent, svgJob );
179 return dlg.ShowModal() == wxID_OK;
180 } );
181 Register( "gencad",
182 std::bind( &PCBNEW_JOBS_HANDLER::JobExportGencad, this, std::placeholders::_1 ),
183 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
184 {
185 JOB_EXPORT_PCB_GENCAD* gencadJob = dynamic_cast<JOB_EXPORT_PCB_GENCAD*>( job );
186
187 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
188 false ) );
189
190 wxCHECK( gencadJob && editFrame, false );
191
192 DIALOG_GENCAD_EXPORT_OPTIONS dlg( editFrame, gencadJob->GetSettingsDialogTitle(), gencadJob );
193 return dlg.ShowModal() == wxID_OK;
194 } );
195 Register( "dxf", std::bind( &PCBNEW_JOBS_HANDLER::JobExportDxf, this, std::placeholders::_1 ),
196 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
197 {
198 JOB_EXPORT_PCB_DXF* dxfJob = dynamic_cast<JOB_EXPORT_PCB_DXF*>( job );
199
200 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
201 false ) );
202
203 wxCHECK( dxfJob && editFrame, false );
204
205 DIALOG_PLOT dlg( editFrame, aParent, dxfJob );
206 return dlg.ShowModal() == wxID_OK;
207 } );
208 Register( "pdf", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPdf, this, std::placeholders::_1 ),
209 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
210 {
211 JOB_EXPORT_PCB_PDF* pdfJob = dynamic_cast<JOB_EXPORT_PCB_PDF*>( job );
212
213 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
214 false ) );
215
216 wxCHECK( pdfJob && editFrame, false );
217
218 DIALOG_PLOT dlg( editFrame, aParent, pdfJob );
219 return dlg.ShowModal() == wxID_OK;
220 } );
221 Register( "png", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPng, this, std::placeholders::_1 ),
222 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
223 {
224 JOB_EXPORT_PCB_PNG* pngJob = dynamic_cast<JOB_EXPORT_PCB_PNG*>( job );
225
226 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
227 false ) );
228
229 wxCHECK( pngJob && editFrame, false );
230
231 DIALOG_PLOT dlg( editFrame, aParent, pngJob );
232 return dlg.ShowModal() == wxID_OK;
233 } );
234 Register( "ps", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPs, this, std::placeholders::_1 ),
235 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
236 {
237 JOB_EXPORT_PCB_PS* psJob = dynamic_cast<JOB_EXPORT_PCB_PS*>( job );
238
239 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
240 false ) );
241
242 wxCHECK( psJob && editFrame, false );
243
244 DIALOG_PLOT dlg( editFrame, aParent, psJob );
245 return dlg.ShowModal() == wxID_OK;
246 } );
247 Register( "stats",
248 std::bind( &PCBNEW_JOBS_HANDLER::JobExportStats, this, std::placeholders::_1 ),
249 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
250 {
251 JOB_EXPORT_PCB_STATS* statsJob = dynamic_cast<JOB_EXPORT_PCB_STATS*>( job );
252
253 PCB_EDIT_FRAME* editFrame =
254 dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR, false ) );
255
256 wxCHECK( statsJob && editFrame, false );
257
258 if( statsJob->m_filename.IsEmpty() && editFrame->GetBoard() )
259 {
260 wxFileName boardName = editFrame->GetBoard()->GetFileName();
261 statsJob->m_filename = boardName.GetFullPath();
262 }
263
264 wxWindow* parent = aParent ? aParent : static_cast<wxWindow*>( editFrame );
265
266 DIALOG_BOARD_STATS_JOB dlg( parent, statsJob );
267
268 return dlg.ShowModal() == wxID_OK;
269 } );
270 Register( "gerber",
271 std::bind( &PCBNEW_JOBS_HANDLER::JobExportGerber, this, std::placeholders::_1 ),
272 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
273 {
274 JOB_EXPORT_PCB_GERBER* gJob = dynamic_cast<JOB_EXPORT_PCB_GERBER*>( 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( "gerbers",
285 std::bind( &PCBNEW_JOBS_HANDLER::JobExportGerbers, this, std::placeholders::_1 ),
286 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
287 {
288 JOB_EXPORT_PCB_GERBERS* gJob = dynamic_cast<JOB_EXPORT_PCB_GERBERS*>( job );
289
290 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
291 false ) );
292
293 wxCHECK( gJob && editFrame, false );
294
295 DIALOG_PLOT dlg( editFrame, aParent, gJob );
296 return dlg.ShowModal() == wxID_OK;
297 } );
298 Register( "hpgl",
299 [&]( JOB* aJob )
300 {
301 m_reporter->Report( _( "Plotting to HPGL is no longer supported as of KiCad 10.0.\n" ),
304 },
305 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
306 {
307 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
308 false ) );
309
310 wxCHECK( editFrame, false );
311
312 DisplayErrorMessage( editFrame,
313 _( "Plotting to HPGL is no longer supported as of KiCad 10.0." ) );
314 return false;
315 } );
316 Register( "drill",
317 std::bind( &PCBNEW_JOBS_HANDLER::JobExportDrill, this, std::placeholders::_1 ),
318 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
319 {
320 JOB_EXPORT_PCB_DRILL* drillJob = dynamic_cast<JOB_EXPORT_PCB_DRILL*>( job );
321
322 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
323 false ) );
324
325 wxCHECK( drillJob && editFrame, false );
326
327 DIALOG_GENDRILL dlg( editFrame, drillJob, aParent );
328 return dlg.ShowModal() == wxID_OK;
329 } );
330 Register( "pos", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPos, this, std::placeholders::_1 ),
331 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
332 {
333 JOB_EXPORT_PCB_POS* posJob = dynamic_cast<JOB_EXPORT_PCB_POS*>( job );
334
335 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
336 false ) );
337
338 wxCHECK( posJob && editFrame, false );
339
340 DIALOG_GEN_FOOTPRINT_POSITION dlg( posJob, editFrame, aParent );
341 return dlg.ShowModal() == wxID_OK;
342 } );
343 Register( "fpupgrade",
344 std::bind( &PCBNEW_JOBS_HANDLER::JobExportFpUpgrade, this, std::placeholders::_1 ),
345 []( JOB* job, wxWindow* aParent ) -> bool
346 {
347 return true;
348 } );
349 Register( "fpsvg",
350 std::bind( &PCBNEW_JOBS_HANDLER::JobExportFpSvg, this, std::placeholders::_1 ),
351 []( JOB* job, wxWindow* aParent ) -> bool
352 {
353 return true;
354 } );
355 Register( "drc", std::bind( &PCBNEW_JOBS_HANDLER::JobExportDrc, this, std::placeholders::_1 ),
356 []( JOB* job, wxWindow* aParent ) -> bool
357 {
358 JOB_PCB_DRC* drcJob = dynamic_cast<JOB_PCB_DRC*>( job );
359
360 wxCHECK( drcJob, false );
361
362 DIALOG_DRC_JOB_CONFIG dlg( aParent, drcJob );
363 return dlg.ShowModal() == wxID_OK;
364 } );
365 Register( "ipc2581",
366 std::bind( &PCBNEW_JOBS_HANDLER::JobExportIpc2581, this, std::placeholders::_1 ),
367 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
368 {
369 JOB_EXPORT_PCB_IPC2581* ipcJob = dynamic_cast<JOB_EXPORT_PCB_IPC2581*>( job );
370
371 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
372 false ) );
373
374 wxCHECK( ipcJob && editFrame, false );
375
376 DIALOG_EXPORT_2581 dlg( ipcJob, editFrame, aParent );
377 return dlg.ShowModal() == wxID_OK;
378 } );
379 Register( "ipcd356",
380 std::bind( &PCBNEW_JOBS_HANDLER::JobExportIpcD356, this, std::placeholders::_1 ),
381 []( JOB* job, wxWindow* aParent ) -> bool
382 {
383 return true;
384 } );
385 Register( "odb",
386 std::bind( &PCBNEW_JOBS_HANDLER::JobExportOdb, this, std::placeholders::_1 ),
387 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
388 {
389 JOB_EXPORT_PCB_ODB* odbJob = dynamic_cast<JOB_EXPORT_PCB_ODB*>( job );
390
391 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
392 false ) );
393
394 wxCHECK( odbJob && editFrame, false );
395
396 DIALOG_EXPORT_ODBPP dlg( odbJob, editFrame, aParent );
397 return dlg.ShowModal() == wxID_OK;
398 } );
399}
400
401
405
406
408{
409 delete m_cliBoard;
410 m_cliBoard = nullptr;
411 m_toolManager.reset();
412}
413
414
416{
417 TOOL_MANAGER* toolManager = nullptr;
418 if( Pgm().IsGUI() )
419 {
420 // we assume the PCB we are working on here is the one in the frame
421 // so use the frame's tool manager
422 PCB_EDIT_FRAME* editFrame = (PCB_EDIT_FRAME*) m_kiway->Player( FRAME_PCB_EDITOR, false );
423 if( editFrame )
424 toolManager = editFrame->GetToolManager();
425 }
426 else
427 {
428 if( m_toolManager == nullptr )
429 {
430 m_toolManager = std::make_unique<TOOL_MANAGER>();
431 }
432
433 toolManager = m_toolManager.get();
434
435 toolManager->SetEnvironment( aBrd, nullptr, nullptr, Kiface().KifaceSettings(), nullptr );
436 }
437 return toolManager;
438}
439
440
441BOARD* PCBNEW_JOBS_HANDLER::getBoard( const wxString& aPath )
442{
443 BOARD* brd = nullptr;
444 SETTINGS_MANAGER& settingsManager = Pgm().GetSettingsManager();
445
446 auto getProjectForBoard =
447 [&]( const wxString& aBoardPath ) -> PROJECT*
448 {
449 wxFileName pro = aBoardPath;
450 pro.SetExt( FILEEXT::ProjectFileExtension );
451 pro.MakeAbsolute();
452
453 PROJECT* project = settingsManager.GetProject( pro.GetFullPath() );
454
455 if( !project )
456 {
457 settingsManager.LoadProject( pro.GetFullPath(), true );
458 project = settingsManager.GetProject( pro.GetFullPath() );
459 }
460
461 return project;
462 };
463
464 auto loadBoardFromPath =
465 [&]( const wxString& aBoardPath ) -> BOARD*
466 {
467 PROJECT* project = getProjectForBoard( aBoardPath );
468
469 PCB_IO_MGR::PCB_FILE_T pluginType =
471
472 if( !project || pluginType == PCB_IO_MGR::FILE_TYPE_NONE )
473 return nullptr;
474
475 try
476 {
477 std::unique_ptr<BOARD> loadedBoard = BOARD_LOADER::Load( aBoardPath, pluginType,
478 project );
479 return loadedBoard.release();
480 }
481 catch ( ... )
482 {
483 return nullptr;
484 }
485 };
486
487 if( !Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpen() )
488 {
489 wxString pcbPath = aPath;
490
491 if( pcbPath.IsEmpty() )
492 {
493 wxFileName path = Pgm().GetSettingsManager().Prj().GetProjectFullName();
495 path.MakeAbsolute();
496 pcbPath = path.GetFullPath();
497 }
498
499 if( !m_cliBoard )
500 m_cliBoard = loadBoardFromPath( pcbPath );
501
502 brd = m_cliBoard;
503 }
504 else if( Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpen() )
505 {
506 PCB_EDIT_FRAME* editFrame = (PCB_EDIT_FRAME*) m_kiway->Player( FRAME_PCB_EDITOR, false );
507
508 if( editFrame )
509 brd = editFrame->GetBoard();
510 }
511 else
512 {
513 brd = loadBoardFromPath( aPath );
514 }
515
516 if( !brd )
517 m_reporter->Report( _( "Failed to load board\n" ), RPT_SEVERITY_ERROR );
518
519 return brd;
520}
521
522
523LSEQ PCBNEW_JOBS_HANDLER::convertLayerArg( wxString& aLayerString, BOARD* aBoard ) const
524{
525 std::map<wxString, LSET> layerUserMasks;
526 std::map<wxString, LSET> layerMasks;
527 std::map<wxString, LSET> layerGuiMasks;
528
529 // Build list of layer names and their layer mask:
530 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
531 {
532 // Add user layer name
533 if( aBoard )
534 layerUserMasks[ aBoard->GetLayerName( layer ) ] = LSET( { layer } );
535
536 // Add layer name used in pcb files
537 layerMasks[ LSET::Name( layer ) ] = LSET( { layer } );
538 // Add layer name using GUI canonical layer name
539 layerGuiMasks[ LayerName( layer ) ] = LSET( { layer } );
540 }
541
542 // Add list of grouped layer names used in pcb files
543 layerMasks[ wxT( "*" ) ] = LSET::AllLayersMask();
544 layerMasks[ wxT( "*.Cu" ) ] = LSET::AllCuMask();
545 layerMasks[ wxT( "*In.Cu" ) ] = LSET::InternalCuMask();
546 layerMasks[ wxT( "F&B.Cu" ) ] = LSET( { F_Cu, B_Cu } );
547 layerMasks[ wxT( "*.Adhes" ) ] = LSET( { B_Adhes, F_Adhes } );
548 layerMasks[ wxT( "*.Paste" ) ] = LSET( { B_Paste, F_Paste } );
549 layerMasks[ wxT( "*.Mask" ) ] = LSET( { B_Mask, F_Mask } );
550 layerMasks[ wxT( "*.SilkS" ) ] = LSET( { B_SilkS, F_SilkS } );
551 layerMasks[ wxT( "*.Fab" ) ] = LSET( { B_Fab, F_Fab } );
552 layerMasks[ wxT( "*.CrtYd" ) ] = LSET( { B_CrtYd, F_CrtYd } );
553
554 // Add list of grouped layer names using GUI canonical layer names
555 layerGuiMasks[ wxT( "*.Adhesive" ) ] = LSET( { B_Adhes, F_Adhes } );
556 layerGuiMasks[ wxT( "*.Silkscreen" ) ] = LSET( { B_SilkS, F_SilkS } );
557 layerGuiMasks[ wxT( "*.Courtyard" ) ] = LSET( { B_CrtYd, F_CrtYd } );
558
559 LSEQ layerMask;
560
561 auto pushLayers =
562 [&]( const LSET& layerSet )
563 {
564 for( PCB_LAYER_ID layer : layerSet.Seq() )
565 layerMask.push_back( layer );
566 };
567
568 if( !aLayerString.IsEmpty() )
569 {
570 wxStringTokenizer layerTokens( aLayerString, "," );
571
572 while( layerTokens.HasMoreTokens() )
573 {
574 std::string token = TO_UTF8( layerTokens.GetNextToken().Trim( true ).Trim( false ) );
575
576 if( layerUserMasks.contains( token ) )
577 pushLayers( layerUserMasks.at( token ) );
578 else if( layerMasks.count( token ) )
579 pushLayers( layerMasks.at( token ) );
580 else if( layerGuiMasks.count( token ) )
581 pushLayers( layerGuiMasks.at( token ) );
582 else
583 m_reporter->Report( wxString::Format( _( "Invalid layer name '%s'\n" ), token ) );
584 }
585 }
586
587 return layerMask;
588}
589
590
592{
593 JOB_EXPORT_PCB_3D* aStepJob = dynamic_cast<JOB_EXPORT_PCB_3D*>( aJob );
594
595 if( aStepJob == nullptr )
597
598 BOARD* brd = getBoard( aStepJob->m_filename );
599
600 if( !brd )
602
603 if( !aStepJob->m_variant.IsEmpty() )
604 brd->SetCurrentVariant( aStepJob->m_variant );
605
606 if( aStepJob->GetConfiguredOutputPath().IsEmpty() )
607 {
608 wxFileName fn = brd->GetFileName();
609 fn.SetName( fn.GetName() );
610
611 switch( aStepJob->m_format )
612 {
622 default:
623 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
624 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
625 }
626
627 aStepJob->SetWorkingOutputPath( fn.GetFullName() );
628 }
629
630 wxString outPath = resolveJobOutputPath( aJob, brd );
631
632 if( !PATHS::EnsurePathExists( outPath, true ) )
633 {
634 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
636 }
637
639 {
640
641 double scale = 0.0;
642 switch ( aStepJob->m_vrmlUnits )
643 {
644 case JOB_EXPORT_PCB_3D::VRML_UNITS::MM: scale = 1.0; break;
645 case JOB_EXPORT_PCB_3D::VRML_UNITS::METERS: scale = 0.001; break;
646 case JOB_EXPORT_PCB_3D::VRML_UNITS::TENTHS: scale = 10.0 / 25.4; break;
647 case JOB_EXPORT_PCB_3D::VRML_UNITS::INCH: scale = 1.0 / 25.4; break;
648 }
649
650 EXPORTER_VRML vrmlExporter( brd );
651 wxString messages;
652
653 double originX = pcbIUScale.IUTomm( aStepJob->m_3dparams.m_Origin.x );
654 double originY = pcbIUScale.IUTomm( aStepJob->m_3dparams.m_Origin.y );
655
656 if( !aStepJob->m_hasUserOrigin )
657 {
658 BOX2I bbox = brd->ComputeBoundingBox( true, true );
659 originX = pcbIUScale.IUTomm( bbox.GetCenter().x );
660 originY = pcbIUScale.IUTomm( bbox.GetCenter().y );
661 }
662
663 bool success = vrmlExporter.ExportVRML_File( brd->GetProject(),
664 &messages,
665 outPath,
666 scale,
668 aStepJob->m_3dparams.m_IncludeDNP,
669 !aStepJob->m_vrmlModelDir.IsEmpty(),
670 aStepJob->m_vrmlRelativePaths,
671 aStepJob->m_vrmlModelDir,
672 originX,
673 originY );
674
675 if ( success )
676 {
677 m_reporter->Report( wxString::Format( _( "Successfully exported VRML to %s" ),
678 outPath ),
680 }
681 else
682 {
683 m_reporter->Report( _( "Error exporting VRML" ), RPT_SEVERITY_ERROR );
685 }
686 }
687 else
688 {
689 EXPORTER_STEP_PARAMS params = aStepJob->m_3dparams;
690
691 switch( aStepJob->m_format )
692 {
702 default:
703 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
704 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
705 }
706
707 EXPORTER_STEP stepExporter( brd, params, m_reporter );
708 stepExporter.m_outputFile = aStepJob->GetFullOutputPath( brd->GetProject() );
709
710 if( !stepExporter.Export() )
712 }
713
714 return CLI::EXIT_CODES::OK;
715}
716
717
719{
720 JOB_PCB_RENDER* aRenderJob = dynamic_cast<JOB_PCB_RENDER*>( aJob );
721
722 if( aRenderJob == nullptr )
724
725 // Reject width and height being invalid
726 // Final bit of sanity because this can blow things up
727 if( aRenderJob->m_width <= 0 || aRenderJob->m_height <= 0 )
728 {
729 m_reporter->Report( _( "Invalid image dimensions" ), RPT_SEVERITY_ERROR );
731 }
732
733 BOARD* brd = getBoard( aRenderJob->m_filename );
734
735 if( !brd )
737
738 if( !aRenderJob->m_variant.IsEmpty() )
739 brd->SetCurrentVariant( aRenderJob->m_variant );
740
741 if( aRenderJob->GetConfiguredOutputPath().IsEmpty() )
742 {
743 wxFileName fn = brd->GetFileName();
744
745 switch( aRenderJob->m_format )
746 {
749 default:
750 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
751 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
752 }
753
754 // set the name to board name + "side", its lazy but its hard to generate anything truely unique
755 // incase someone is doing this in a jobset with multiple jobs, they should be setting the output themselves
756 // or we do a hash based on all the options
757 fn.SetName( wxString::Format( "%s-%d", fn.GetName(), static_cast<int>( aRenderJob->m_side ) ) );
758
759 aRenderJob->SetWorkingOutputPath( fn.GetFullName() );
760 }
761
762 wxString outPath = resolveJobOutputPath( aJob, brd );
763
764 if( !PATHS::EnsurePathExists( outPath, true ) )
765 {
766 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
768 }
769
770 BOARD_ADAPTER boardAdapter;
771
772 boardAdapter.SetBoard( brd );
773 boardAdapter.m_IsBoardView = false;
774
776
778 {
779 cfg.m_Render = userCfg->m_Render;
780 cfg.m_Camera = userCfg->m_Camera;
781 cfg.m_LayerPresets = userCfg->m_LayerPresets;
782 }
783
784 if( aRenderJob->m_appearancePreset.empty() )
785 {
786 // Force display 3D models
788 cfg.m_Render.show_footprints_dnp = true;
792 }
793
794 if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::BASIC )
795 {
796 // Silkscreen is pixelated without antialiasing
798
799 cfg.m_Render.raytrace_backfloor = aRenderJob->m_floor;
800 cfg.m_Render.raytrace_post_processing = aRenderJob->m_floor;
801
803 cfg.m_Render.raytrace_reflections = false;
804 cfg.m_Render.raytrace_shadows = aRenderJob->m_floor;
805
806 // Better colors
808
809 // Tracks below soldermask are not visible without refractions
812 }
813 else if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::HIGH )
814 {
816 cfg.m_Render.raytrace_backfloor = true;
820 cfg.m_Render.raytrace_shadows = true;
823 }
824 else if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::JOB_SETTINGS )
825 {
827 cfg.m_Render.raytrace_backfloor = aRenderJob->m_floor;
830 }
831
833 aRenderJob->m_lightTopIntensity.y,
834 aRenderJob->m_lightTopIntensity.z, 1.0 );
835
837 aRenderJob->m_lightBottomIntensity.y,
838 aRenderJob->m_lightBottomIntensity.z, 1.0 );
839
841 aRenderJob->m_lightCameraIntensity.y,
842 aRenderJob->m_lightCameraIntensity.z, 1.0 );
843
844 COLOR4D lightColor( aRenderJob->m_lightSideIntensity.x,
845 aRenderJob->m_lightSideIntensity.y,
846 aRenderJob->m_lightSideIntensity.z, 1.0 );
847
849 lightColor, lightColor, lightColor, lightColor,
850 lightColor, lightColor, lightColor, lightColor,
851 };
852
853 int sideElevation = aRenderJob->m_lightSideElevation;
854
856 sideElevation, sideElevation, sideElevation, sideElevation,
857 -sideElevation, -sideElevation, -sideElevation, -sideElevation,
858 };
859
861 45, 135, 225, 315, 45, 135, 225, 315,
862 };
863
864 cfg.m_CurrentPreset = aRenderJob->m_appearancePreset;
866 boardAdapter.m_Cfg = &cfg;
867
868 // Apply the preset's layer visibility and colors to the render settings
869 if( !aRenderJob->m_appearancePreset.empty() )
870 {
871 wxString presetName = wxString::FromUTF8( aRenderJob->m_appearancePreset );
872
873 if( presetName == FOLLOW_PCB || presetName == FOLLOW_PLOT_SETTINGS )
874 {
875 boardAdapter.SetVisibleLayers( boardAdapter.GetVisibleLayers() );
876 }
877 else if( LAYER_PRESET_3D* preset = cfg.FindPreset( presetName ) )
878 {
879 boardAdapter.SetVisibleLayers( preset->layers );
880 boardAdapter.SetLayerColors( preset->colors );
881
882 if( preset->name.Lower() == _( "legacy colors" ) )
883 cfg.m_UseStackupColors = false;
884 }
885 }
886
889 && aRenderJob->m_format == JOB_PCB_RENDER::FORMAT::PNG ) )
890 {
891 boardAdapter.m_ColorOverrides[LAYER_3D_BACKGROUND_TOP] = COLOR4D( 1.0, 1.0, 1.0, 0.0 );
892 boardAdapter.m_ColorOverrides[LAYER_3D_BACKGROUND_BOTTOM] = COLOR4D( 1.0, 1.0, 1.0, 0.0 );
893 }
894
896
897 static std::map<JOB_PCB_RENDER::SIDE, VIEW3D_TYPE> s_viewCmdMap = {
904 };
905
908
909 wxSize windowSize( aRenderJob->m_width, aRenderJob->m_height );
910 TRACK_BALL camera( 2 * RANGE_SCALE_3D );
911
912 camera.SetProjection( projection );
913 camera.SetCurWindowSize( windowSize );
914
915 RENDER_3D_RAYTRACE_RAM raytrace( boardAdapter, camera );
916 raytrace.SetCurWindowSize( windowSize );
917
918 for( bool first = true; raytrace.Redraw( false, m_reporter, m_reporter ); first = false )
919 {
920 if( first )
921 {
922 const float cmTo3D = boardAdapter.BiuTo3dUnits() * pcbIUScale.mmToIU( 10.0 );
923
924 // First redraw resets lookat point to the board center, so set up the camera here
925 camera.ViewCommand_T1( s_viewCmdMap[aRenderJob->m_side] );
926
927 camera.SetLookAtPos_T1( camera.GetLookAtPos_T1() + SFVEC3F( aRenderJob->m_pivot.x,
928 aRenderJob->m_pivot.y,
929 aRenderJob->m_pivot.z ) * cmTo3D );
930
931 camera.Pan_T1( SFVEC3F( aRenderJob->m_pan.x, aRenderJob->m_pan.y, aRenderJob->m_pan.z ) );
932
933 camera.Zoom_T1( aRenderJob->m_zoom );
934
935 camera.RotateX_T1( DEG2RAD( aRenderJob->m_rotation.x ) );
936 camera.RotateY_T1( DEG2RAD( aRenderJob->m_rotation.y ) );
937 camera.RotateZ_T1( DEG2RAD( aRenderJob->m_rotation.z ) );
938
939 camera.Interpolate( 1.0f );
940 camera.SetT0_and_T1_current_T();
941 camera.ParametersChanged();
942 }
943 }
944
945 uint8_t* rgbaBuffer = raytrace.GetBuffer();
946 wxSize realSize = raytrace.GetRealBufferSize();
947 bool success = !!rgbaBuffer;
948
949 if( rgbaBuffer )
950 {
951 const unsigned int wxh = realSize.x * realSize.y;
952
953 unsigned char* rgbBuffer = (unsigned char*) malloc( wxh * 3 );
954 unsigned char* alphaBuffer = (unsigned char*) malloc( wxh );
955
956 unsigned char* rgbaPtr = rgbaBuffer;
957 unsigned char* rgbPtr = rgbBuffer;
958 unsigned char* alphaPtr = alphaBuffer;
959
960 for( int y = 0; y < realSize.y; y++ )
961 {
962 for( int x = 0; x < realSize.x; x++ )
963 {
964 rgbPtr[0] = rgbaPtr[0];
965 rgbPtr[1] = rgbaPtr[1];
966 rgbPtr[2] = rgbaPtr[2];
967 alphaPtr[0] = rgbaPtr[3];
968
969 rgbaPtr += 4;
970 rgbPtr += 3;
971 alphaPtr += 1;
972 }
973 }
974
975 wxImage image( realSize );
976 image.SetData( rgbBuffer );
977 image.SetAlpha( alphaBuffer );
978 image = image.Mirror( false );
979
980 image.SetOption( wxIMAGE_OPTION_QUALITY, 90 );
981 image.SaveFile( outPath, aRenderJob->m_format == JOB_PCB_RENDER::FORMAT::PNG ? wxBITMAP_TYPE_PNG
982 : wxBITMAP_TYPE_JPEG );
983 }
984
985 if( success )
986 {
987 m_reporter->Report( _( "Successfully created 3D render image" ) + wxS( "\n" ), RPT_SEVERITY_INFO );
988 return CLI::EXIT_CODES::OK;
989 }
990 else
991 {
992 m_reporter->Report( _( "Error creating 3D render image" ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
994 }
995}
996
997
999{
1000 JOB_EXPORT_PCB_SVG* aSvgJob = dynamic_cast<JOB_EXPORT_PCB_SVG*>( aJob );
1001
1002 if( aSvgJob == nullptr )
1004
1005 BOARD* brd = getBoard( aSvgJob->m_filename );
1006 TOOL_MANAGER* toolManager = getToolManager( brd );
1007
1008 if( !brd )
1010
1011 if( !aSvgJob->m_variant.IsEmpty() )
1012 brd->SetCurrentVariant( aSvgJob->m_variant );
1013
1015 {
1016 if( aSvgJob->GetConfiguredOutputPath().IsEmpty() )
1017 {
1018 wxFileName fn = brd->GetFileName();
1019 fn.SetName( fn.GetName() );
1021
1022 aSvgJob->SetWorkingOutputPath( fn.GetFullName() );
1023 }
1024 }
1025
1026 wxString outPath = resolveJobOutputPath( aJob, brd, &aSvgJob->m_drawingSheet );
1027
1029 {
1030 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1032 }
1033
1034 if( aSvgJob->m_checkZonesBeforePlot )
1035 {
1036 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1037 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1038
1039 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1040 }
1041
1042 if( aSvgJob->m_argLayers )
1043 aSvgJob->m_plotLayerSequence = convertLayerArg( aSvgJob->m_argLayers.value(), brd );
1044
1045 if( aSvgJob->m_argCommonLayers )
1046 aSvgJob->m_plotOnAllLayersSequence = convertLayerArg( aSvgJob->m_argCommonLayers.value(), brd );
1047
1048 if( aSvgJob->m_plotLayerSequence.size() < 1 )
1049 {
1050 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1052 }
1053
1054 PCB_PLOT_PARAMS plotOpts;
1055 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aSvgJob, *m_reporter );
1056
1057 PCB_PLOTTER plotter( brd, m_reporter, plotOpts );
1058
1059 std::optional<wxString> layerName;
1060 std::optional<wxString> sheetName;
1061 std::optional<wxString> sheetPath;
1062 std::vector<wxString> outputPaths;
1063
1065 {
1066 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1067 layerName = aSvgJob->GetVarOverrides().at( wxT( "LAYER" ) );
1068
1069 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1070 sheetName = aSvgJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1071
1072 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1073 sheetPath = aSvgJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1074 }
1075
1076 if( !plotter.Plot( outPath, aSvgJob->m_plotLayerSequence, aSvgJob->m_plotOnAllLayersSequence,
1078 layerName, sheetName, sheetPath, &outputPaths ) )
1079 {
1081 }
1082
1083 for( const wxString& outputPath : outputPaths )
1084 aSvgJob->AddOutput( outputPath );
1085
1086 return CLI::EXIT_CODES::OK;
1087}
1088
1089
1091{
1092 JOB_EXPORT_PCB_DXF* aDxfJob = dynamic_cast<JOB_EXPORT_PCB_DXF*>( aJob );
1093
1094 if( aDxfJob == nullptr )
1096
1097 BOARD* brd = getBoard( aDxfJob->m_filename );
1098
1099 if( !brd )
1101
1102 if( !aDxfJob->m_variant.IsEmpty() )
1103 brd->SetCurrentVariant( aDxfJob->m_variant );
1104
1105 TOOL_MANAGER* toolManager = getToolManager( brd );
1106
1107 if( aDxfJob->m_checkZonesBeforePlot )
1108 {
1109 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1110 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1111
1112 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1113 }
1114
1115 if( aDxfJob->m_argLayers )
1116 aDxfJob->m_plotLayerSequence = convertLayerArg( aDxfJob->m_argLayers.value(), brd );
1117
1118 if( aDxfJob->m_argCommonLayers )
1119 aDxfJob->m_plotOnAllLayersSequence = convertLayerArg( aDxfJob->m_argCommonLayers.value(), brd );
1120
1121 if( aDxfJob->m_plotLayerSequence.size() < 1 )
1122 {
1123 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1125 }
1126
1128 {
1129 if( aDxfJob->GetConfiguredOutputPath().IsEmpty() )
1130 {
1131 wxFileName fn = brd->GetFileName();
1132 fn.SetName( fn.GetName() );
1134
1135 aDxfJob->SetWorkingOutputPath( fn.GetFullName() );
1136 }
1137 }
1138
1139 wxString outPath = resolveJobOutputPath( aJob, brd, &aDxfJob->m_drawingSheet );
1140
1142 {
1143 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1145 }
1146
1147 PCB_PLOT_PARAMS plotOpts;
1148 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aDxfJob, *m_reporter);
1149
1150 PCB_PLOTTER plotter( brd, m_reporter, plotOpts );
1151
1152 std::optional<wxString> layerName;
1153 std::optional<wxString> sheetName;
1154 std::optional<wxString> sheetPath;
1155
1157 {
1158 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1159 layerName = aDxfJob->GetVarOverrides().at( wxT( "LAYER" ) );
1160
1161 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1162 sheetName = aDxfJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1163
1164 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1165 sheetPath = aDxfJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1166 }
1167
1168 std::vector<wxString> outputPaths;
1169
1170 if( !plotter.Plot( outPath, aDxfJob->m_plotLayerSequence, aDxfJob->m_plotOnAllLayersSequence,
1172 layerName, sheetName, sheetPath, &outputPaths ) )
1173 {
1175 }
1176
1177 for( const wxString& outputPath : outputPaths )
1178 aJob->AddOutput( outputPath );
1179
1180 return CLI::EXIT_CODES::OK;
1181}
1182
1183
1185{
1186 bool plotAllLayersOneFile = false;
1187 JOB_EXPORT_PCB_PDF* pdfJob = dynamic_cast<JOB_EXPORT_PCB_PDF*>( aJob );
1188
1189 if( pdfJob == nullptr )
1191
1192 BOARD* brd = getBoard( pdfJob->m_filename );
1193
1194 if( !brd )
1196
1197 if( !pdfJob->m_variant.IsEmpty() )
1198 brd->SetCurrentVariant( pdfJob->m_variant );
1199
1200 TOOL_MANAGER* toolManager = getToolManager( brd );
1201
1202 if( pdfJob->m_checkZonesBeforePlot )
1203 {
1204 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1205 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1206
1207 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1208 }
1209
1210 if( pdfJob->m_argLayers )
1211 pdfJob->m_plotLayerSequence = convertLayerArg( pdfJob->m_argLayers.value(), brd );
1212
1213 if( pdfJob->m_argCommonLayers )
1214 pdfJob->m_plotOnAllLayersSequence = convertLayerArg( pdfJob->m_argCommonLayers.value(), brd );
1215
1217 plotAllLayersOneFile = true;
1218
1219 if( pdfJob->m_plotLayerSequence.size() < 1 )
1220 {
1221 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1223 }
1224
1225 const bool outputIsSingle = plotAllLayersOneFile || pdfJob->m_pdfSingle;
1226
1227 if( outputIsSingle && pdfJob->GetConfiguredOutputPath().IsEmpty() )
1228 {
1229 wxFileName fn = brd->GetFileName();
1230 fn.SetName( fn.GetName() );
1232
1233 pdfJob->SetWorkingOutputPath( fn.GetFullName() );
1234 }
1235
1236 wxString outPath = resolveJobOutputPath( pdfJob, brd, &pdfJob->m_drawingSheet );
1237
1238 PCB_PLOT_PARAMS plotOpts;
1239 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, pdfJob, *m_reporter );
1240
1241 PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts );
1242
1243 if( !PATHS::EnsurePathExists( outPath, outputIsSingle ) )
1244 {
1245 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1247 }
1248
1249 std::optional<wxString> layerName;
1250 std::optional<wxString> sheetName;
1251 std::optional<wxString> sheetPath;
1252
1253 if( plotAllLayersOneFile )
1254 {
1255 if( pdfJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1256 layerName = pdfJob->GetVarOverrides().at( wxT( "LAYER" ) );
1257
1258 if( pdfJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1259 sheetName = pdfJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1260
1261 if( pdfJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1262 sheetPath = pdfJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1263 }
1264
1265 std::vector<wxString> outputPaths;
1266
1267 if( !pcbPlotter.Plot( outPath, pdfJob->m_plotLayerSequence,
1268 pdfJob->m_plotOnAllLayersSequence, false, outputIsSingle,
1269 layerName, sheetName, sheetPath, &outputPaths ) )
1270 {
1272 }
1273
1274 for( const wxString& outputPath : outputPaths )
1275 aJob->AddOutput( outputPath );
1276
1277 return CLI::EXIT_CODES::OK;
1278}
1279
1280
1282{
1283 JOB_EXPORT_PCB_PNG* pngJob = dynamic_cast<JOB_EXPORT_PCB_PNG*>( aJob );
1284
1285 if( pngJob == nullptr )
1287
1288 BOARD* brd = getBoard( pngJob->m_filename );
1289
1290 if( !brd )
1292
1293 if( !pngJob->m_variant.IsEmpty() )
1294 brd->SetCurrentVariant( pngJob->m_variant );
1295
1296 TOOL_MANAGER* toolManager = getToolManager( brd );
1297
1298 if( pngJob->m_checkZonesBeforePlot )
1299 {
1300 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1301 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1302
1303 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1304 }
1305
1306 if( pngJob->m_argLayers )
1307 pngJob->m_plotLayerSequence = convertLayerArg( pngJob->m_argLayers.value(), brd );
1308
1309 if( pngJob->m_argCommonLayers )
1310 pngJob->m_plotOnAllLayersSequence = convertLayerArg( pngJob->m_argCommonLayers.value(), brd );
1311
1312 if( pngJob->m_plotLayerSequence.size() < 1 )
1313 {
1314 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1316 }
1317
1318 if( pngJob->GetConfiguredOutputPath().IsEmpty() )
1319 {
1320 wxFileName fn = brd->GetFileName();
1321 fn.SetName( fn.GetName() );
1323
1324 pngJob->SetWorkingOutputPath( fn.GetFullName() );
1325 }
1326
1327 wxString outPath = resolveJobOutputPath( pngJob, brd, &pngJob->m_drawingSheet );
1328
1329 PCB_PLOT_PARAMS plotOpts;
1330 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, pngJob, *m_reporter );
1331
1332 PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts );
1333
1334 if( !PATHS::EnsurePathExists( outPath, false ) )
1335 {
1336 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1338 }
1339
1340 std::vector<wxString> outputPaths;
1341
1342 if( !pcbPlotter.Plot( outPath, pngJob->m_plotLayerSequence,
1343 pngJob->m_plotOnAllLayersSequence, false, false,
1344 std::nullopt, std::nullopt, std::nullopt, &outputPaths ) )
1345 {
1347 }
1348
1349 for( const wxString& outputPath : outputPaths )
1350 aJob->AddOutput( outputPath );
1351
1352 return CLI::EXIT_CODES::OK;
1353}
1354
1355
1357{
1358 JOB_EXPORT_PCB_PS* psJob = dynamic_cast<JOB_EXPORT_PCB_PS*>( aJob );
1359
1360 if( psJob == nullptr )
1362
1363 BOARD* brd = getBoard( psJob->m_filename );
1364
1365 if( !brd )
1367
1368 if( !psJob->m_variant.IsEmpty() )
1369 brd->SetCurrentVariant( psJob->m_variant );
1370
1371 TOOL_MANAGER* toolManager = getToolManager( brd );
1372
1373 if( psJob->m_checkZonesBeforePlot )
1374 {
1375 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1376 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1377
1378 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1379 }
1380
1381 if( psJob->m_argLayers )
1382 psJob->m_plotLayerSequence = convertLayerArg( psJob->m_argLayers.value(), brd );
1383
1384 if( psJob->m_argCommonLayers )
1385 psJob->m_plotOnAllLayersSequence = convertLayerArg( psJob->m_argCommonLayers.value(), brd );
1386
1387 if( psJob->m_plotLayerSequence.size() < 1 )
1388 {
1389 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1391 }
1392
1393 bool isSingle = psJob->m_genMode == JOB_EXPORT_PCB_PS::GEN_MODE::SINGLE;
1394
1395 if( isSingle )
1396 {
1397 if( psJob->GetConfiguredOutputPath().IsEmpty() )
1398 {
1399 wxFileName fn = brd->GetFileName();
1400 fn.SetName( fn.GetName() );
1402
1403 psJob->SetWorkingOutputPath( fn.GetFullName() );
1404 }
1405 }
1406
1407 wxString outPath = resolveJobOutputPath( psJob, brd, &psJob->m_drawingSheet );
1408
1409 if( !PATHS::EnsurePathExists( outPath, isSingle ) )
1410 {
1411 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1413 }
1414
1415 PCB_PLOT_PARAMS plotOpts;
1416 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, psJob, *m_reporter );
1417
1418 PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts );
1419
1420 std::optional<wxString> layerName;
1421 std::optional<wxString> sheetName;
1422 std::optional<wxString> sheetPath;
1423
1424 if( isSingle )
1425 {
1426 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1427 layerName = psJob->GetVarOverrides().at( wxT( "LAYER" ) );
1428
1429 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1430 sheetName = psJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1431
1432 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1433 sheetPath = psJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1434 }
1435
1436 std::vector<wxString> outputPaths;
1437
1438 if( !pcbPlotter.Plot( outPath, psJob->m_plotLayerSequence, psJob->m_plotOnAllLayersSequence, false, isSingle,
1439 layerName, sheetName, sheetPath, &outputPaths ) )
1440 {
1442 }
1443
1444 for( const wxString& outputPath : outputPaths )
1445 aJob->AddOutput( outputPath );
1446
1447 return CLI::EXIT_CODES::OK;
1448}
1449
1450
1452{
1453 int exitCode = CLI::EXIT_CODES::OK;
1454 JOB_EXPORT_PCB_GERBERS* aGerberJob = dynamic_cast<JOB_EXPORT_PCB_GERBERS*>( aJob );
1455
1456 if( aGerberJob == nullptr )
1458
1459 BOARD* brd = getBoard( aGerberJob->m_filename );
1460
1461 if( !brd )
1463
1464 if( !aGerberJob->m_variant.IsEmpty() )
1465 brd->SetCurrentVariant( aGerberJob->m_variant );
1466
1467 wxString outPath = resolveJobOutputPath( aJob, brd, &aGerberJob->m_drawingSheet );
1468
1469 if( !PATHS::EnsurePathExists( outPath, false ) )
1470 {
1471 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1473 }
1474
1475 TOOL_MANAGER* toolManager = getToolManager( brd );
1476
1477 if( aGerberJob->m_checkZonesBeforePlot )
1478 {
1479 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1480 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1481
1482 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1483 }
1484
1485 bool hasLayerListSpecified = false; // will be true if the user layer list is not empty
1486
1487 if( aGerberJob->m_argLayers )
1488 {
1489 if( !aGerberJob->m_argLayers.value().empty() )
1490 {
1491 aGerberJob->m_plotLayerSequence = convertLayerArg( aGerberJob->m_argLayers.value(), brd );
1492 hasLayerListSpecified = true;
1493 }
1494 else
1495 {
1497 }
1498 }
1499
1500 if( aGerberJob->m_argCommonLayers )
1501 aGerberJob->m_plotOnAllLayersSequence = convertLayerArg( aGerberJob->m_argCommonLayers.value(), brd );
1502
1503 PCB_PLOT_PARAMS boardPlotOptions = brd->GetPlotOptions();
1504 GERBER_JOBFILE_WRITER jobfile_writer( brd );
1505
1506 wxString fileExt;
1507
1508 if( aGerberJob->m_useBoardPlotParams )
1509 {
1510 // The board plot options are saved with all copper layers enabled, even those that don't
1511 // exist in the current stackup. This is done so the layers are automatically enabled in the plot
1512 // dialog when the user enables them. We need to filter out these not-enabled layers here so
1513 // we don't plot 32 layers when we only have 4, etc.
1514 LSET plotLayers = ( boardPlotOptions.GetLayerSelection() & LSET::AllNonCuMask() )
1515 | ( brd->GetEnabledLayers() & LSET::AllCuMask() );
1516 aGerberJob->m_plotLayerSequence = plotLayers.SeqStackupForPlotting();
1517 aGerberJob->m_plotOnAllLayersSequence = boardPlotOptions.GetPlotOnAllLayersSequence();
1518 }
1519 else
1520 {
1521 // default to the board enabled layers, but only if the user has not specifed a layer list
1522 // ( m_plotLayerSequence can be empty with a broken user layer list)
1523 if( aGerberJob->m_plotLayerSequence.empty() && !hasLayerListSpecified )
1525 }
1526
1527 // Ensure layers to plot are restricted to enabled layers of the board to plot
1528 LSET layersToPlot = LSET( { aGerberJob->m_plotLayerSequence } ) & brd->GetEnabledLayers();
1529
1530 for( PCB_LAYER_ID layer : layersToPlot.UIOrder() )
1531 {
1532 LSEQ plotSequence;
1533
1534 // Base layer always gets plotted first.
1535 plotSequence.push_back( layer );
1536
1537 // Now all the "include on all" layers
1538 for( PCB_LAYER_ID layer_all : aGerberJob->m_plotOnAllLayersSequence )
1539 {
1540 // Don't plot the same layer more than once;
1541 if( find( plotSequence.begin(), plotSequence.end(), layer_all ) != plotSequence.end() )
1542 continue;
1543
1544 plotSequence.push_back( layer_all );
1545 }
1546
1547 // Pick the basename from the board file
1548 wxFileName fn( brd->GetFileName() );
1549 wxString layerName = brd->GetLayerName( layer );
1550 wxString sheetName;
1551 wxString sheetPath;
1552 PCB_PLOT_PARAMS plotOpts;
1553
1554 if( aGerberJob->m_useBoardPlotParams )
1555 plotOpts = boardPlotOptions;
1556 else
1557 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aGerberJob, *m_reporter );
1558
1559 if( plotOpts.GetUseGerberProtelExtensions() )
1560 fileExt = GetGerberProtelExtension( layer );
1561 else
1563
1564 PCB_PLOTTER::BuildPlotFileName( &fn, outPath, layerName, fileExt );
1565 wxString fullname = fn.GetFullName();
1566
1567 if( m_progressReporter )
1568 {
1569 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), fullname ) );
1570 m_progressReporter->KeepRefreshing();
1571 }
1572
1573 jobfile_writer.AddGbrFile( layer, fullname );
1574
1575 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1576 layerName = aJob->GetVarOverrides().at( wxT( "LAYER" ) );
1577
1578 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1579 sheetName = aJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1580
1581 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1582 sheetPath = aJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1583
1584 // We are feeding it one layer at the start here to silence a logic check
1585 GERBER_PLOTTER* plotter;
1586 plotter = (GERBER_PLOTTER*) StartPlotBoard( brd, &plotOpts, layer, layerName,
1587 fn.GetFullPath(), sheetName, sheetPath );
1588
1589 if( plotter )
1590 {
1591 m_reporter->Report( wxString::Format( _( "Plotted to '%s'.\n" ), fn.GetFullPath() ),
1593
1594 PlotBoardLayers( brd, plotter, plotSequence, plotOpts );
1595 plotter->EndPlot();
1596 aJob->AddOutput( fn.GetFullPath() );
1597 }
1598 else
1599 {
1600 m_reporter->Report( wxString::Format( _( "Failed to plot to '%s'.\n" ), fn.GetFullPath() ),
1603 }
1604
1605 delete plotter;
1606 }
1607
1608 if( aGerberJob->m_createJobsFile )
1609 {
1610 wxFileName fn( brd->GetFileName() );
1611
1612 // Build gerber job file from basename
1614 jobfile_writer.CreateJobFile( fn.GetFullPath() );
1615 aJob->AddOutput( fn.GetFullPath() );
1616 }
1617
1618 return exitCode;
1619}
1620
1621
1623{
1624 JOB_EXPORT_PCB_GENCAD* aGencadJob = dynamic_cast<JOB_EXPORT_PCB_GENCAD*>( aJob );
1625
1626 if( aGencadJob == nullptr )
1628
1629 BOARD* brd = getBoard( aGencadJob->m_filename );
1630
1631 if( !brd )
1633
1634 GENCAD_EXPORTER exporter( brd );
1635
1636 VECTOR2I GencadOffset;
1637 VECTOR2I auxOrigin = brd->GetDesignSettings().GetAuxOrigin();
1638 GencadOffset.x = aGencadJob->m_useDrillOrigin ? auxOrigin.x : 0;
1639 GencadOffset.y = aGencadJob->m_useDrillOrigin ? auxOrigin.y : 0;
1640
1641 exporter.FlipBottomPads( aGencadJob->m_flipBottomPads );
1642 exporter.UsePinNamesUnique( aGencadJob->m_useUniquePins );
1643 exporter.UseIndividualShapes( aGencadJob->m_useIndividualShapes );
1644 exporter.SetPlotOffet( GencadOffset );
1645 exporter.StoreOriginCoordsInFile( aGencadJob->m_storeOriginCoords );
1646
1647 if( aGencadJob->GetConfiguredOutputPath().IsEmpty() )
1648 {
1649 wxFileName fn = brd->GetFileName();
1650 fn.SetName( fn.GetName() );
1651 fn.SetExt( FILEEXT::GencadFileExtension );
1652
1653 aGencadJob->SetWorkingOutputPath( fn.GetFullName() );
1654 }
1655
1656 wxString outPath = resolveJobOutputPath( aJob, brd );
1657
1658 if( !PATHS::EnsurePathExists( outPath, true ) )
1659 {
1660 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1662 }
1663
1664 if( !exporter.WriteFile( outPath ) )
1665 {
1666 m_reporter->Report( wxString::Format( _( "Failed to create file '%s'.\n" ), outPath ),
1668
1670 }
1671
1672 aJob->AddOutput( outPath );
1673 m_reporter->Report( _( "Successfully created genCAD file\n" ), RPT_SEVERITY_INFO );
1674
1675 return CLI::EXIT_CODES::OK;
1676}
1677
1678
1680{
1681 JOB_EXPORT_PCB_STATS* statsJob = dynamic_cast<JOB_EXPORT_PCB_STATS*>( aJob );
1682
1683 if( statsJob == nullptr )
1685
1686 BOARD* brd = getBoard( statsJob->m_filename );
1687
1688 if( !brd )
1690
1693
1698
1699 ComputeBoardStatistics( brd, options, data );
1700
1701 wxString projectName;
1702
1703 if( brd->GetProject() )
1704 projectName = brd->GetProject()->GetProjectName();
1705
1706 wxFileName boardFile = brd->GetFileName();
1707
1708 if( boardFile.GetName().IsEmpty() )
1709 boardFile = wxFileName( statsJob->m_filename );
1710
1712 UNITS_PROVIDER unitsProvider( pcbIUScale, unitsForReport );
1713
1714 wxString report;
1715
1717 report = FormatBoardStatisticsJson( data, brd, unitsProvider, projectName, boardFile.GetName() );
1718 else
1719 report = FormatBoardStatisticsReport( data, brd, unitsProvider, projectName, boardFile.GetName() );
1720
1721 if( statsJob->GetConfiguredOutputPath().IsEmpty() && statsJob->GetWorkingOutputPath().IsEmpty() )
1722 statsJob->SetDefaultOutputPath( boardFile.GetFullPath() );
1723
1724 wxString outPath = resolveJobOutputPath( aJob, brd );
1725
1726 if( !PATHS::EnsurePathExists( outPath, true ) )
1727 {
1728 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1730 }
1731
1732 FILE* outFile = wxFopen( outPath, wxS( "wt" ) );
1733
1734 if( !outFile )
1735 {
1736 m_reporter->Report( wxString::Format( _( "Failed to create file '%s'.\n" ), outPath ), RPT_SEVERITY_ERROR );
1738 }
1739
1740 if( fprintf( outFile, "%s", TO_UTF8( report ) ) < 0 )
1741 {
1742 fclose( outFile );
1743 m_reporter->Report( wxString::Format( _( "Error writing file '%s'.\n" ), outPath ), RPT_SEVERITY_ERROR );
1745 }
1746
1747 fclose( outFile );
1748
1749 m_reporter->Report( wxString::Format( _( "Wrote board statistics to '%s'.\n" ), outPath ), RPT_SEVERITY_ACTION );
1750
1751 statsJob->AddOutput( outPath );
1752
1753 return CLI::EXIT_CODES::OK;
1754}
1755
1756
1758{
1759 int exitCode = CLI::EXIT_CODES::OK;
1760 JOB_EXPORT_PCB_GERBER* aGerberJob = dynamic_cast<JOB_EXPORT_PCB_GERBER*>( aJob );
1761
1762 if( aGerberJob == nullptr )
1764
1765 BOARD* brd = getBoard( aGerberJob->m_filename );
1766
1767 if( !brd )
1769
1770 if( !aGerberJob->m_variant.IsEmpty() )
1771 brd->SetCurrentVariant( aGerberJob->m_variant );
1772
1773 TOOL_MANAGER* toolManager = getToolManager( brd );
1774
1775 if( aGerberJob->m_argLayers )
1776 aGerberJob->m_plotLayerSequence = convertLayerArg( aGerberJob->m_argLayers.value(), brd );
1777
1778 if( aGerberJob->m_argCommonLayers )
1779 aGerberJob->m_plotOnAllLayersSequence = convertLayerArg( aGerberJob->m_argCommonLayers.value(), brd );
1780
1781 if( aGerberJob->m_plotLayerSequence.size() < 1 )
1782 {
1783 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1785 }
1786
1787 if( aGerberJob->GetConfiguredOutputPath().IsEmpty() )
1788 {
1789 wxFileName fn = brd->GetFileName();
1790 fn.SetName( fn.GetName() );
1792
1793 aGerberJob->SetWorkingOutputPath( fn.GetFullName() );
1794 }
1795
1796 wxString outPath = resolveJobOutputPath( aJob, brd );
1797
1798 if( aGerberJob->m_checkZonesBeforePlot )
1799 {
1800 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1801 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1802
1803 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1804 }
1805
1806 PCB_PLOT_PARAMS plotOpts;
1807 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aGerberJob, *m_reporter );
1808 plotOpts.SetLayerSelection( aGerberJob->m_plotLayerSequence );
1810
1812 wxString layerName;
1813 wxString sheetName;
1814 wxString sheetPath;
1815
1816 // The first layer will be treated as the layer name for the gerber header,
1817 // the other layers will be treated equivalent to the "Plot on All Layers" option
1818 // in the GUI
1819 if( aGerberJob->m_plotLayerSequence.size() >= 1 )
1820 {
1821 layer = aGerberJob->m_plotLayerSequence.front();
1822 layerName = brd->GetLayerName( layer );
1823 }
1824
1825 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1826 layerName = aJob->GetVarOverrides().at( wxT( "LAYER" ) );
1827
1828 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1829 sheetName = aJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1830
1831 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1832 sheetPath = aJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1833
1834 // We are feeding it one layer at the start here to silence a logic check
1835 PLOTTER* plotter = StartPlotBoard( brd, &plotOpts, layer, layerName, outPath, sheetName,
1836 sheetPath );
1837
1838 if( plotter )
1839 {
1840 PlotBoardLayers( brd, plotter, aGerberJob->m_plotLayerSequence, plotOpts );
1841 plotter->EndPlot();
1842 }
1843 else
1844 {
1845 m_reporter->Report( wxString::Format( _( "Failed to plot to '%s'.\n" ), outPath ),
1848 }
1849
1850 delete plotter;
1851
1852 return exitCode;
1853}
1854
1857
1858
1860{
1861 JOB_EXPORT_PCB_DRILL* aDrillJob = dynamic_cast<JOB_EXPORT_PCB_DRILL*>( aJob );
1862
1863 if( aDrillJob == nullptr )
1865
1866 BOARD* brd = getBoard( aDrillJob->m_filename );
1867
1868 if( !brd )
1870
1871 wxString outPath = resolveJobOutputPath( aJob, brd );
1872
1873 if( !PATHS::EnsurePathExists( outPath ) )
1874 {
1875 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1877 }
1878
1879 std::unique_ptr<GENDRILL_WRITER_BASE> drillWriter;
1880
1882 drillWriter = std::make_unique<EXCELLON_WRITER>( brd );
1883 else
1884 drillWriter = std::make_unique<GERBER_WRITER>( brd );
1885
1886 VECTOR2I offset;
1887
1889 offset = VECTOR2I( 0, 0 );
1890 else
1891 offset = brd->GetDesignSettings().GetAuxOrigin();
1892
1893 PLOT_FORMAT mapFormat = PLOT_FORMAT::PDF;
1894
1895 switch( aDrillJob->m_mapFormat )
1896 {
1901 default:
1903 }
1904
1905
1906 if( aDrillJob->m_generateReport && aDrillJob->m_reportPath.IsEmpty() )
1907 {
1908 wxFileName fn = outPath;
1909 fn.SetFullName( brd->GetFileName() );
1910 fn.SetName( fn.GetName() + "-drill" );
1911 fn.SetExt( FILEEXT::ReportFileExtension );
1912
1913 aDrillJob->m_reportPath = fn.GetFullPath();
1914 }
1915
1917 {
1919
1920 switch( aDrillJob->m_zeroFormat )
1921 {
1924 break;
1925
1928 break;
1929
1932 break;
1933
1935 default:
1937 break;
1938 }
1939
1940 DRILL_PRECISION precision;
1941
1943 precision = precisionListForInches;
1944 else
1945 precision = precisionListForMetric;
1946
1947 EXCELLON_WRITER* excellonWriter = dynamic_cast<EXCELLON_WRITER*>( drillWriter.get() );
1948
1949 if( excellonWriter == nullptr )
1951
1952 excellonWriter->SetFormat( aDrillJob->m_drillUnits == JOB_EXPORT_PCB_DRILL::DRILL_UNITS::MM,
1953 zeroFmt, precision.m_Lhs, precision.m_Rhs );
1954 excellonWriter->SetOptions( aDrillJob->m_excellonMirrorY,
1955 aDrillJob->m_excellonMinimalHeader,
1956 offset, aDrillJob->m_excellonCombinePTHNPTH );
1957 excellonWriter->SetRouteModeForOvalHoles( aDrillJob->m_excellonOvalDrillRoute );
1958 excellonWriter->SetMapFileFormat( mapFormat );
1959
1960 if( !excellonWriter->CreateDrillandMapFilesSet( outPath, true, aDrillJob->m_generateMap,
1961 m_reporter ) )
1962 {
1964 }
1965
1966 aDrillJob->AddOutput( outPath );
1967
1968 if( aDrillJob->m_generateReport )
1969 {
1970 wxString reportPath = aDrillJob->ResolveOutputPath( aDrillJob->m_reportPath, true, brd->GetProject() );
1971
1972 if( !excellonWriter->GenDrillReportFile( reportPath ) )
1973 {
1975 }
1976
1977 aDrillJob->AddOutput( reportPath );
1978 }
1979 }
1981 {
1982 GERBER_WRITER* gerberWriter = dynamic_cast<GERBER_WRITER*>( drillWriter.get() );
1983
1984 if( gerberWriter == nullptr )
1986
1987 // Set gerber precision: only 5 or 6 digits for mantissa are allowed
1988 // (SetFormat() accept 5 or 6, and any other value set the precision to 5)
1989 // the integer part precision is always 4, and units always mm
1990 gerberWriter->SetFormat( aDrillJob->m_gerberPrecision );
1991 gerberWriter->SetOptions( offset );
1992 gerberWriter->SetMapFileFormat( mapFormat );
1993
1994 if( !gerberWriter->CreateDrillandMapFilesSet( outPath, true, aDrillJob->m_generateMap,
1995 aDrillJob->m_generateTenting, m_reporter ) )
1996 {
1998 }
1999
2000 aDrillJob->AddOutput( outPath );
2001
2002 if( aDrillJob->m_generateReport )
2003 {
2004 wxString reportPath = aDrillJob->ResolveOutputPath( aDrillJob->m_reportPath, true, brd->GetProject() );
2005
2006 if( !gerberWriter->GenDrillReportFile( reportPath ) )
2007 {
2009 }
2010
2011 aDrillJob->AddOutput( reportPath );
2012 }
2013 }
2014
2015 return CLI::EXIT_CODES::OK;
2016}
2017
2018
2020{
2021 JOB_EXPORT_PCB_POS* aPosJob = dynamic_cast<JOB_EXPORT_PCB_POS*>( aJob );
2022
2023 if( aPosJob == nullptr )
2025
2026 BOARD* brd = getBoard( aPosJob->m_filename );
2027
2028 if( !brd )
2030
2031 if( aPosJob->GetConfiguredOutputPath().IsEmpty() )
2032 {
2033 wxFileName fn = brd->GetFileName();
2034 fn.SetName( fn.GetName() );
2035
2038 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV )
2039 fn.SetExt( FILEEXT::CsvFileExtension );
2040 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::GERBER )
2041 fn.SetExt( FILEEXT::GerberFileExtension );
2042
2043 aPosJob->SetWorkingOutputPath( fn.GetFullName() );
2044 }
2045
2046 wxString outPath = resolveJobOutputPath( aJob, brd );
2047
2048 if( !PATHS::EnsurePathExists( outPath, true ) )
2049 {
2050 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2052 }
2053
2056 {
2057 wxFileName fn( outPath );
2058 wxString baseName = fn.GetName();
2059
2060 auto exportPlaceFile =
2061 [&]( bool frontSide, bool backSide, const wxString& curr_outPath ) -> bool
2062 {
2063 FILE* file = wxFopen( curr_outPath, wxS( "wt" ) );
2064 wxCHECK( file, false );
2065
2066 PLACE_FILE_EXPORTER exporter( brd,
2068 aPosJob->m_smdOnly,
2070 aPosJob->m_excludeDNP,
2071 aPosJob->m_excludeBOM,
2072 frontSide,
2073 backSide,
2076 aPosJob->m_negateBottomX );
2077
2078 // Set variant for variant-aware DNP/BOM/position file filtering
2079 exporter.SetVariant( aPosJob->m_variant );
2080
2081 std::string data = exporter.GenPositionData();
2082 fputs( data.c_str(), file );
2083 fclose( file );
2084
2085 return true;
2086 };
2087
2088 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH && !aPosJob->m_singleFile )
2089 {
2090 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( baseName, true, false ) );
2091
2092 if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV && !aPosJob->m_nakedFilename )
2093 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
2094
2095 if( exportPlaceFile( true, false, fn.GetFullPath() ) )
2096 {
2097 m_reporter->Report( wxString::Format( _( "Wrote front position data to '%s'.\n" ),
2098 fn.GetFullPath() ),
2100
2101 aPosJob->AddOutput( fn.GetFullPath() );
2102 }
2103 else
2104 {
2106 }
2107
2108 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( baseName, false, true ) );
2109
2110 if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV && !aPosJob->m_nakedFilename )
2111 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
2112
2113 if( exportPlaceFile( false, true, fn.GetFullPath() ) )
2114 {
2115 m_reporter->Report( wxString::Format( _( "Wrote back position data to '%s'.\n" ),
2116 fn.GetFullPath() ),
2118
2119 aPosJob->AddOutput( fn.GetFullPath() );
2120 }
2121 else
2122 {
2124 }
2125 }
2126 else
2127 {
2128 bool front = aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::FRONT
2130
2131 bool back = aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BACK
2133
2134 if( !aPosJob->m_nakedFilename )
2135 {
2136 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( fn.GetName(), front, back ) );
2137
2139 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
2140 }
2141
2142 if( exportPlaceFile( front, back, fn.GetFullPath() ) )
2143 {
2144 m_reporter->Report( wxString::Format( _( "Wrote position data to '%s'.\n" ),
2145 fn.GetFullPath() ),
2147
2148 aPosJob->AddOutput( fn.GetFullPath() );
2149 }
2150 else
2151 {
2153 }
2154 }
2155 }
2156 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::GERBER )
2157 {
2158 PLACEFILE_GERBER_WRITER exporter( brd );
2159
2160 // Set variant for variant-aware DNP/BOM/position file filtering
2161 exporter.SetVariant( aPosJob->m_variant );
2162
2163 PCB_LAYER_ID gbrLayer = F_Cu;
2164 wxString outPath_base = outPath;
2165
2167 || aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH )
2168 {
2169 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH || !aPosJob->m_nakedFilename )
2170 outPath = exporter.GetPlaceFileName( outPath, gbrLayer );
2171
2172 if( exporter.CreatePlaceFile( outPath, gbrLayer, aPosJob->m_gerberBoardEdge,
2173 aPosJob->m_excludeDNP, aPosJob->m_excludeBOM ) >= 0 )
2174 {
2175 m_reporter->Report( wxString::Format( _( "Wrote front position data to '%s'.\n" ), outPath ),
2177
2178 aPosJob->AddOutput( outPath );
2179 }
2180 else
2181 {
2183 }
2184 }
2185
2187 || aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH )
2188 {
2189 gbrLayer = B_Cu;
2190
2191 outPath = outPath_base;
2192
2193 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH || !aPosJob->m_nakedFilename )
2194 outPath = exporter.GetPlaceFileName( outPath, gbrLayer );
2195
2196 if( exporter.CreatePlaceFile( outPath, gbrLayer, aPosJob->m_gerberBoardEdge,
2197 aPosJob->m_excludeDNP, aPosJob->m_excludeBOM ) >= 0 )
2198 {
2199 m_reporter->Report( wxString::Format( _( "Wrote back position data to '%s'.\n" ), outPath ),
2201
2202 aPosJob->AddOutput( outPath );
2203 }
2204 else
2205 {
2207 }
2208 }
2209 }
2210
2211 return CLI::EXIT_CODES::OK;
2212}
2213
2214
2216{
2217 JOB_FP_UPGRADE* upgradeJob = dynamic_cast<JOB_FP_UPGRADE*>( aJob );
2218
2219 if( upgradeJob == nullptr )
2221
2223
2224 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
2225 {
2226 if( wxFile::Exists( upgradeJob->m_outputLibraryPath )
2227 || wxDir::Exists( upgradeJob->m_outputLibraryPath) )
2228 {
2229 m_reporter->Report( _( "Output path must not conflict with existing path\n" ),
2232 }
2233 }
2234 else if( fileType != PCB_IO_MGR::KICAD_SEXP )
2235 {
2236 m_reporter->Report( _( "Output path must be specified to convert legacy and non-KiCad libraries\n" ),
2238
2240 }
2241
2243 {
2244 if( !wxDir::Exists( upgradeJob->m_libraryPath ) )
2245 {
2246 m_reporter->Report( _( "Footprint library path does not exist or is not accessible\n" ),
2249 }
2250
2252 FP_CACHE fpLib( &pcb_io, upgradeJob->m_libraryPath );
2253
2254 try
2255 {
2256 fpLib.Load();
2257 }
2258 catch( ... )
2259 {
2260 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
2262 }
2263
2264 if( m_progressReporter )
2265 m_progressReporter->KeepRefreshing();
2266
2267 bool shouldSave = upgradeJob->m_force;
2268
2269 for( const auto& footprint : fpLib.GetFootprints() )
2270 {
2271 if( footprint.second->GetFootprint()->GetFileFormatVersionAtLoad() < SEXPR_BOARD_FILE_VERSION )
2272 shouldSave = true;
2273 }
2274
2275 if( shouldSave )
2276 {
2277 try
2278 {
2279 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
2280 fpLib.SetPath( upgradeJob->m_outputLibraryPath );
2281
2282 fpLib.Save();
2283 }
2284 catch( ... )
2285 {
2286 m_reporter->Report( _( "Unable to save library\n" ), RPT_SEVERITY_ERROR );
2288 }
2289 }
2290 else
2291 {
2292 m_reporter->Report( _( "Footprint library was not updated\n" ), RPT_SEVERITY_ERROR );
2293 }
2294 }
2295 else
2296 {
2297 if( !PCB_IO_MGR::ConvertLibrary( {}, upgradeJob->m_libraryPath,
2298 upgradeJob->m_outputLibraryPath, nullptr /* REPORTER */ ) )
2299 {
2300 m_reporter->Report( ( "Unable to convert library\n" ), RPT_SEVERITY_ERROR );
2302 }
2303 }
2304
2305 return CLI::EXIT_CODES::OK;
2306}
2307
2308
2310{
2311 JOB_FP_EXPORT_SVG* svgJob = dynamic_cast<JOB_FP_EXPORT_SVG*>( aJob );
2312
2313 if( svgJob == nullptr )
2315
2317 FP_CACHE fpLib( &pcb_io, svgJob->m_libraryPath );
2318
2319 if( svgJob->m_argLayers )
2320 {
2321 if( !svgJob->m_argLayers.value().empty() )
2322 svgJob->m_plotLayerSequence = convertLayerArg( svgJob->m_argLayers.value(), nullptr );
2323 else
2325 }
2326
2327 try
2328 {
2329 fpLib.Load();
2330 }
2331 catch( ... )
2332 {
2333 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
2335 }
2336
2337 wxString outPath = svgJob->GetFullOutputPath( nullptr );
2338
2339 if( !PATHS::EnsurePathExists( outPath, true ) )
2340 {
2341 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2343 }
2344
2345 int exitCode = CLI::EXIT_CODES::OK;
2346 bool singleFpPlotted = false;
2347
2348 for( const auto& [fpName, fpCacheEntry] : fpLib.GetFootprints() )
2349 {
2350 if( m_progressReporter )
2351 {
2352 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), fpName ) );
2353 m_progressReporter->KeepRefreshing();
2354 }
2355
2356 if( !svgJob->m_footprint.IsEmpty() )
2357 {
2358 // skip until we find the right footprint
2359 if( fpName != svgJob->m_footprint )
2360 continue;
2361 else
2362 singleFpPlotted = true;
2363 }
2364
2365 exitCode = doFpExportSvg( svgJob, fpCacheEntry->GetFootprint().get() );
2366
2367 if( exitCode != CLI::EXIT_CODES::OK )
2368 break;
2369 }
2370
2371 if( !svgJob->m_footprint.IsEmpty() && !singleFpPlotted )
2372 {
2373 m_reporter->Report( _( "The given footprint could not be found to export." ) + wxS( "\n" ),
2375 }
2376
2377 return CLI::EXIT_CODES::OK;
2378}
2379
2380
2382{
2383 // the hack for now is we create fake boards containing the footprint and plot the board
2384 // until we refactor better plot api later
2385 std::unique_ptr<BOARD> brd = BOARD_LOADER::CreateEmptyBoard( Pgm().GetSettingsManager().GetProject( "" ) );
2386 brd->GetProject()->ApplyTextVars( aSvgJob->GetVarOverrides() );
2387 brd->SynchronizeProperties();
2388
2389 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aFootprint->Clone() );
2390
2391 if( fp == nullptr )
2393
2394 fp->SetLink( niluuid );
2395 fp->SetFlags( IS_NEW );
2396 fp->SetParent( brd.get() );
2397
2398 for( PAD* pad : fp->Pads() )
2399 {
2400 pad->SetLocalRatsnestVisible( false );
2401 pad->SetNetCode( 0 );
2402 }
2403
2404 fp->SetOrientation( ANGLE_0 );
2405 fp->SetPosition( VECTOR2I( 0, 0 ) );
2406
2407 brd->Add( fp, ADD_MODE::INSERT, true );
2408
2409 wxFileName outputFile;
2410 outputFile.SetPath( aSvgJob->GetFullOutputPath(nullptr) );
2411 outputFile.SetName( aFootprint->GetFPID().GetLibItemName().wx_str() );
2412 outputFile.SetExt( FILEEXT::SVGFileExtension );
2413
2414 m_reporter->Report( wxString::Format( _( "Plotting footprint '%s' to '%s'\n" ),
2415 aFootprint->GetFPID().GetLibItemName().wx_str(),
2416 outputFile.GetFullPath() ),
2418
2419 PCB_PLOT_PARAMS plotOpts;
2420 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aSvgJob, *m_reporter );
2421
2422 // always fixed for the svg plot
2423 plotOpts.SetPlotFrameRef( false );
2424 plotOpts.SetSvgFitPageToBoard( true );
2425 plotOpts.SetMirror( false );
2426 plotOpts.SetSkipPlotNPTH_Pads( false );
2427
2428 if( plotOpts.GetSketchPadsOnFabLayers() )
2429 {
2430 plotOpts.SetPlotPadNumbers( true );
2431 }
2432
2433 PCB_PLOTTER plotter( brd.get(), m_reporter, plotOpts );
2434
2435 if( !plotter.Plot( outputFile.GetFullPath(),
2436 aSvgJob->m_plotLayerSequence,
2438 false,
2439 true,
2440 wxEmptyString, wxEmptyString,
2441 wxEmptyString ) )
2442 {
2443 m_reporter->Report( _( "Error creating svg file" ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
2445 }
2446
2447 aSvgJob->AddOutput( outputFile.GetFullPath() );
2448
2449 return CLI::EXIT_CODES::OK;
2450}
2451
2452
2454{
2455 JOB_PCB_DRC* drcJob = dynamic_cast<JOB_PCB_DRC*>( aJob );
2456
2457 if( drcJob == nullptr )
2459
2460 BOARD* brd = getBoard( drcJob->m_filename );
2461
2462 if( !brd )
2464
2465 // Running DRC requires libraries be loaded, so make sure they have been
2467 adapter->AsyncLoad();
2468 adapter->BlockUntilLoaded();
2469
2470 if( drcJob->GetConfiguredOutputPath().IsEmpty() )
2471 {
2472 wxFileName fn = brd->GetFileName();
2473 fn.SetName( fn.GetName() + wxS( "-drc" ) );
2474
2476 fn.SetExt( FILEEXT::JsonFileExtension );
2477 else
2478 fn.SetExt( FILEEXT::ReportFileExtension );
2479
2480 drcJob->SetWorkingOutputPath( fn.GetFullName() );
2481 }
2482
2483 wxString outPath = resolveJobOutputPath( aJob, brd );
2484
2485 if( !PATHS::EnsurePathExists( outPath, true ) )
2486 {
2487 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2489 }
2490
2491 EDA_UNITS units;
2492
2493 switch( drcJob->m_units )
2494 {
2495 case JOB_PCB_DRC::UNITS::INCH: units = EDA_UNITS::INCH; break;
2496 case JOB_PCB_DRC::UNITS::MILS: units = EDA_UNITS::MILS; break;
2497 case JOB_PCB_DRC::UNITS::MM: units = EDA_UNITS::MM; break;
2498 default: units = EDA_UNITS::MM; break;
2499 }
2500
2501 std::shared_ptr<DRC_ENGINE> drcEngine = brd->GetDesignSettings().m_DRCEngine;
2502 std::unique_ptr<NETLIST> netlist = std::make_unique<NETLIST>();
2503
2504 drcEngine->SetDrawingSheet( getDrawingSheetProxyView( brd ) );
2505
2506 // BOARD_COMMIT uses TOOL_MANAGER to grab the board internally so we must give it one
2507 TOOL_MANAGER* toolManager = getToolManager( brd );
2508
2509 BOARD_COMMIT commit( toolManager );
2510 bool checkParity = drcJob->m_parity;
2511 std::string netlist_str;
2512
2513 if( checkParity )
2514 {
2515 wxString annotateMsg = _( "Schematic parity tests require a fully annotated schematic." );
2516 netlist_str = annotateMsg;
2517
2518 // The KIFACE_NETLIST_SCHEMATIC function has some broken-ness that the schematic
2519 // frame's version does not, but it is the only one that works in CLI, so we use it
2520 // if we don't have the sch frame open.
2521 // TODO: clean this up, see https://gitlab.com/kicad/code/kicad/-/issues/19929
2522 if( m_kiway->Player( FRAME_SCH, false ) )
2523 {
2524 m_kiway->ExpressMail( FRAME_SCH, MAIL_SCH_GET_NETLIST, netlist_str );
2525 }
2526 else
2527 {
2528 wxFileName schematicPath( drcJob->m_filename );
2529 schematicPath.MakeAbsolute();
2530 schematicPath.SetExt( FILEEXT::KiCadSchematicFileExtension );
2531
2532 if( !schematicPath.Exists() )
2533 schematicPath.SetExt( FILEEXT::LegacySchematicFileExtension );
2534
2535 if( !schematicPath.Exists() )
2536 {
2537 m_reporter->Report( _( "Failed to fetch schematic netlist for parity tests.\n" ),
2539 checkParity = false;
2540 }
2541 else
2542 {
2543 typedef bool ( *NETLIST_FN_PTR )( const wxString&, std::string& );
2544 KIFACE* eeschema = m_kiway->KiFACE( KIWAY::FACE_SCH );
2545 NETLIST_FN_PTR netlister =
2546 (NETLIST_FN_PTR) eeschema->IfaceOrAddress( KIFACE_NETLIST_SCHEMATIC );
2547 ( *netlister )( schematicPath.GetFullPath(), netlist_str );
2548 }
2549 }
2550
2551 if( netlist_str == annotateMsg )
2552 {
2553 m_reporter->Report( wxString( netlist_str ) + wxT( "\n" ), RPT_SEVERITY_ERROR );
2554 checkParity = false;
2555 }
2556 }
2557
2558 if( checkParity )
2559 {
2560 try
2561 {
2562 STRING_LINE_READER* lineReader = new STRING_LINE_READER( netlist_str,
2563 _( "Eeschema netlist" ) );
2564 KICAD_NETLIST_READER netlistReader( lineReader, netlist.get() );
2565
2566 netlistReader.LoadNetlist();
2567 }
2568 catch( const IO_ERROR& )
2569 {
2570 m_reporter->Report( _( "Failed to fetch schematic netlist for parity tests.\n" ),
2572 checkParity = false;
2573 }
2574
2575 drcEngine->SetSchematicNetlist( netlist.get() );
2576 }
2577
2578 if( drcJob->m_refillZones )
2579 {
2580 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
2581 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
2582
2583 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
2584 }
2585
2586 drcEngine->SetProgressReporter( m_progressReporter );
2587 drcEngine->SetViolationHandler(
2588 [&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos, int aLayer,
2589 const std::function<void( PCB_MARKER* )>& aPathGenerator )
2590 {
2591 PCB_MARKER* marker = new PCB_MARKER( aItem, aPos, aLayer );
2592 aPathGenerator( marker );
2593 commit.Add( marker );
2594 } );
2595
2596 brd->RecordDRCExclusions();
2597 brd->DeleteMARKERs( true, true );
2598 drcEngine->RunTests( units, drcJob->m_reportAllTrackErrors, checkParity );
2599 drcEngine->ClearViolationHandler();
2600
2601 commit.Push( _( "DRC" ), SKIP_UNDO | SKIP_SET_DIRTY );
2602
2603 // Update the exclusion status on any excluded markers that still exist.
2604 brd->ResolveDRCExclusions( false );
2605
2606 std::shared_ptr<DRC_ITEMS_PROVIDER> markersProvider = std::make_shared<DRC_ITEMS_PROVIDER>(
2608
2609 std::shared_ptr<DRC_ITEMS_PROVIDER> ratsnestProvider =
2610 std::make_shared<DRC_ITEMS_PROVIDER>( brd, MARKER_BASE::MARKER_RATSNEST );
2611
2612 std::shared_ptr<DRC_ITEMS_PROVIDER> fpWarningsProvider =
2613 std::make_shared<DRC_ITEMS_PROVIDER>( brd, MARKER_BASE::MARKER_PARITY );
2614
2615 markersProvider->SetSeverities( drcJob->m_severity );
2616 ratsnestProvider->SetSeverities( drcJob->m_severity );
2617 fpWarningsProvider->SetSeverities( drcJob->m_severity );
2618
2619 m_reporter->Report( wxString::Format( _( "Found %d violations\n" ),
2620 markersProvider->GetCount() ),
2622 m_reporter->Report( wxString::Format( _( "Found %d unconnected items\n" ),
2623 ratsnestProvider->GetCount() ),
2625
2626 if( checkParity )
2627 {
2628 m_reporter->Report( wxString::Format( _( "Found %d schematic parity issues\n" ),
2629 fpWarningsProvider->GetCount() ),
2631 }
2632
2633 DRC_REPORT reportWriter( brd, units, markersProvider, ratsnestProvider, fpWarningsProvider );
2634
2635 bool wroteReport = false;
2636
2638 wroteReport = reportWriter.WriteJsonReport( outPath );
2639 else
2640 wroteReport = reportWriter.WriteTextReport( outPath );
2641
2642 if( !wroteReport )
2643 {
2644 m_reporter->Report( wxString::Format( _( "Unable to save DRC report to %s\n" ), outPath ),
2647 }
2648
2649 drcJob->AddOutput( outPath );
2650
2651 m_reporter->Report( wxString::Format( _( "Saved DRC Report to %s\n" ), outPath ),
2653
2654 if( drcJob->m_refillZones && drcJob->m_saveBoard )
2655 {
2656 if( BOARD_LOADER::SaveBoard( drcJob->m_filename, brd ) )
2657 {
2658 m_reporter->Report( _( "Saved board\n" ), RPT_SEVERITY_ACTION );
2659 }
2660 else
2661 {
2662 m_reporter->Report( _( "Failed to save board.\n" ), RPT_SEVERITY_ERROR );
2663
2665 }
2666 }
2667
2668 if( drcJob->m_exitCodeViolations )
2669 {
2670 if( markersProvider->GetCount() > 0 || ratsnestProvider->GetCount() > 0
2671 || fpWarningsProvider->GetCount() > 0 )
2672 {
2674 }
2675 }
2676
2678}
2679
2680
2682{
2683 JOB_EXPORT_PCB_IPC2581* job = dynamic_cast<JOB_EXPORT_PCB_IPC2581*>( aJob );
2684
2685 if( job == nullptr )
2687
2688 BOARD* brd = getBoard( job->m_filename );
2689
2690 if( !brd )
2692
2693 if( !job->m_variant.IsEmpty() )
2694 brd->SetCurrentVariant( job->m_variant );
2695
2696 if( job->GetConfiguredOutputPath().IsEmpty() )
2697 {
2698 wxFileName fn = brd->GetFileName();
2699 fn.SetExt( job->m_compress ? std::string( "zip" ) : FILEEXT::Ipc2581FileExtension );
2700
2701 job->SetWorkingOutputPath( fn.GetFullName() );
2702 }
2703
2704 wxString outPath = resolveJobOutputPath( aJob, brd );
2705
2706 if( !PATHS::EnsurePathExists( outPath, true ) )
2707 {
2708 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2710 }
2711
2714
2716}
2717
2718
2720{
2721 JOB_EXPORT_PCB_IPCD356* job = dynamic_cast<JOB_EXPORT_PCB_IPCD356*>( aJob );
2722
2723 if( job == nullptr )
2725
2726 BOARD* brd = getBoard( job->m_filename );
2727
2728 if( !brd )
2730
2731 if( job->GetConfiguredOutputPath().IsEmpty() )
2732 {
2733 wxFileName fn = brd->GetFileName();
2734 fn.SetName( fn.GetName() );
2735 fn.SetExt( FILEEXT::IpcD356FileExtension );
2736
2737 job->SetWorkingOutputPath( fn.GetFullName() );
2738 }
2739
2740 wxString outPath = resolveJobOutputPath( aJob, brd );
2741
2742 if( !PATHS::EnsurePathExists( outPath, true ) )
2743 {
2744 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2746 }
2747
2748 IPC356D_WRITER exporter( brd );
2749
2750 bool success = exporter.Write( outPath );
2751
2752 if( success )
2753 {
2754 aJob->AddOutput( outPath );
2755 m_reporter->Report( _( "Successfully created IPC-D-356 file\n" ), RPT_SEVERITY_INFO );
2757 }
2758 else
2759 {
2760 m_reporter->Report( _( "Failed to create IPC-D-356 file\n" ), RPT_SEVERITY_ERROR );
2762 }
2763}
2764
2765
2767{
2768 JOB_EXPORT_PCB_ODB* job = dynamic_cast<JOB_EXPORT_PCB_ODB*>( aJob );
2769
2770 if( job == nullptr )
2772
2773 BOARD* brd = getBoard( job->m_filename );
2774
2775 if( !brd )
2777
2778 if( !job->m_variant.IsEmpty() )
2779 brd->SetCurrentVariant( job->m_variant );
2780
2781 if( job->GetConfiguredOutputPath().IsEmpty() )
2782 {
2784 {
2785 // just basic folder name
2786 job->SetWorkingOutputPath( "odb" );
2787 }
2788 else
2789 {
2790 wxFileName fn( brd->GetFileName() );
2791 fn.SetName( fn.GetName() + wxS( "-odb" ) );
2792
2793 switch( job->m_compressionMode )
2794 {
2796 fn.SetExt( FILEEXT::ArchiveFileExtension );
2797 break;
2798
2800 fn.SetExt( "tgz" );
2801 break;
2802
2803 default:
2804 break;
2805 };
2806
2807 job->SetWorkingOutputPath( fn.GetFullName() );
2808 }
2809 }
2810
2811 wxString outPath = resolveJobOutputPath( job, brd );
2812
2813 // The helper handles output path creation, so hand it a job that already has fully-resolved
2814 // token context (title block and project overrides applied above).
2815 CLI_REPORTER reporter;
2816
2817 if( !m_reporter )
2818 m_reporter = &reporter;
2819
2821 aJob->AddOutput( outPath );
2822
2823 if( m_reporter->HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
2825
2827}
2828
2830{
2831 JOB_PCB_UPGRADE* job = dynamic_cast<JOB_PCB_UPGRADE*>( aJob );
2832
2833 if( job == nullptr )
2835
2836 bool shouldSave = job->m_force;
2837
2838 try
2839 {
2841 BOARD* brd = getBoard( job->m_filename );
2843 shouldSave = true;
2844
2845 if( shouldSave )
2846 {
2847 pi->SaveBoard( brd->GetFileName(), brd );
2848 m_reporter->Report( _( "Successfully saved board file using the latest format\n" ), RPT_SEVERITY_INFO );
2849 }
2850 else
2851 {
2852 m_reporter->Report( _( "Board file was not updated\n" ), RPT_SEVERITY_ERROR );
2853 }
2854 }
2855 catch( const IO_ERROR& ioe )
2856 {
2857 wxString msg =
2858 wxString::Format( _( "Error saving board file '%s'.\n%s" ), job->m_filename, ioe.What().GetData() );
2859 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2861 }
2862
2864}
2865
2866// Most job handlers need to align the running job with the board before resolving any
2867// output paths with variables in them like ${REVISION}.
2868wxString PCBNEW_JOBS_HANDLER::resolveJobOutputPath( JOB* aJob, BOARD* aBoard, const wxString* aDrawingSheet )
2869{
2870 aJob->SetTitleBlock( aBoard->GetTitleBlock() );
2871
2872 if( aDrawingSheet && !aDrawingSheet->IsEmpty() )
2873 loadOverrideDrawingSheet( aBoard, *aDrawingSheet );
2874
2875 PROJECT* project = aBoard->GetProject();
2876
2877 if( project )
2878 project->ApplyTextVars( aJob->GetVarOverrides() );
2879
2880 aBoard->SynchronizeProperties();
2881
2882 return aJob->GetFullOutputPath( project );
2883}
2884
2885
2887{
2889 &aBrd->GetPageSettings(),
2890 aBrd->GetProject(),
2891 &aBrd->GetTitleBlock(),
2892 &aBrd->GetProperties() );
2893
2894 drawingSheet->SetSheetName( std::string() );
2895 drawingSheet->SetSheetPath( std::string() );
2896 drawingSheet->SetIsFirstPage( true );
2897
2898 drawingSheet->SetFileName( TO_UTF8( aBrd->GetFileName() ) );
2899
2900 wxString currentVariant = aBrd->GetCurrentVariant();
2901 wxString variantDesc = aBrd->GetVariantDescription( currentVariant );
2902 drawingSheet->SetVariantName( TO_UTF8( currentVariant ) );
2903 drawingSheet->SetVariantDesc( TO_UTF8( variantDesc ) );
2904
2905 return drawingSheet;
2906}
2907
2908
2909void PCBNEW_JOBS_HANDLER::loadOverrideDrawingSheet( BOARD* aBrd, const wxString& aSheetPath )
2910{
2911 // dont bother attempting to load a empty path, if there was one
2912 if( aSheetPath.IsEmpty() )
2913 return;
2914
2915 auto loadSheet =
2916 [&]( const wxString& path ) -> bool
2917 {
2920 resolver.SetProject( aBrd->GetProject() );
2921 resolver.SetProgramBase( &Pgm() );
2922
2923 wxString filename = resolver.ResolvePath( BASE_SCREEN::m_DrawingSheetFileName,
2924 aBrd->GetProject()->GetProjectPath(),
2925 { aBrd->GetEmbeddedFiles() } );
2926 wxString msg;
2927
2928 if( !DS_DATA_MODEL::GetTheInstance().LoadDrawingSheet( filename, &msg ) )
2929 {
2930 m_reporter->Report( wxString::Format( _( "Error loading drawing sheet '%s'." ),
2931 path )
2932 + wxS( "\n" ) + msg + wxS( "\n" ),
2934 return false;
2935 }
2936
2937 return true;
2938 };
2939
2940 if( loadSheet( aSheetPath ) )
2941 return;
2942
2943 // failed loading custom path, revert back to default
2944 loadSheet( aBrd->GetProject()->GetProjectFile().m_BoardDrawingSheetFile );
2945}
2946
2947
2949{
2950 JOB_PCB_IMPORT* job = dynamic_cast<JOB_PCB_IMPORT*>( aJob );
2951
2952 if( !job )
2954
2955 // Map job format to PCB_IO file type
2957
2958 switch( job->m_format )
2959 {
2962 break;
2963
2966 break;
2967
2970 break;
2971
2974 break;
2975
2978 break;
2979
2982 break;
2983
2986 break;
2987
2990 break;
2991 }
2992
2994 {
2995 m_reporter->Report( wxString::Format( _( "Unable to determine file format for '%s'\n" ),
2996 job->m_inputFile ),
2999 }
3000
3001 // Check that input file exists
3002 if( !wxFile::Exists( job->m_inputFile ) )
3003 {
3004 m_reporter->Report( wxString::Format( _( "Input file not found: '%s'\n" ),
3005 job->m_inputFile ),
3008 }
3009
3010 // Determine output path
3011 wxString outputPath = job->GetConfiguredOutputPath();
3012
3013 if( outputPath.IsEmpty() )
3014 {
3015 wxFileName fn( job->m_inputFile );
3016 fn.SetExt( FILEEXT::KiCadPcbFileExtension );
3017 outputPath = fn.GetFullPath();
3018 }
3019
3020 BOARD* board = nullptr;
3021 wxString formatName = PCB_IO_MGR::ShowType( fileType );
3022 std::vector<wxString> warnings;
3023
3024 try
3025 {
3027
3028 if( !pi )
3029 {
3030 m_reporter->Report( wxString::Format( _( "No plugin found for file type '%s'\n" ),
3031 formatName ),
3034 }
3035
3036 m_reporter->Report( wxString::Format( _( "Importing '%s' using %s format...\n" ),
3037 job->m_inputFile, formatName ),
3039
3040 board = pi->LoadBoard( job->m_inputFile, nullptr, nullptr, nullptr );
3041
3042 if( !board )
3043 {
3044 m_reporter->Report( _( "Failed to load board\n" ), RPT_SEVERITY_ERROR );
3046 }
3047
3048 // Save as KiCad format
3050 kicadPlugin->SaveBoard( outputPath, board );
3051
3052 m_reporter->Report( wxString::Format( _( "Successfully saved imported board to '%s'\n" ),
3053 outputPath ),
3055
3056 // Generate report if requested
3058 {
3059 wxFileName inputFn( job->m_inputFile );
3060 wxFileName outputFn( outputPath );
3061
3062 // Count board statistics
3063 size_t footprintCount = board->Footprints().size();
3064 size_t trackCount = 0;
3065 size_t viaCount = 0;
3066 size_t zoneCount = board->Zones().size();
3067
3068 for( PCB_TRACK* track : board->Tracks() )
3069 {
3070 if( track->Type() == PCB_VIA_T )
3071 viaCount++;
3072 else
3073 trackCount++;
3074 }
3075
3076 // Build layer mapping info
3077 nlohmann::json layerMappings = nlohmann::json::object();
3078 LSEQ enabledLayers = board->GetEnabledLayers().Seq();
3079
3080 for( PCB_LAYER_ID layer : enabledLayers )
3081 {
3082 wxString layerName = board->GetLayerName( layer );
3083
3084 layerMappings[layerName.ToStdString()] = {
3085 { "kicad_layer", LSET::Name( layer ).ToStdString() },
3086 { "method", "auto" }
3087 };
3088 }
3089
3091 {
3092 nlohmann::json report;
3093
3094 report["source_file"] = inputFn.GetFullName().ToStdString();
3095 report["source_format"] = formatName.ToStdString();
3096 report["output_file"] = outputFn.GetFullName().ToStdString();
3097 report["layer_mapping"] = layerMappings;
3098 report["statistics"] = {
3099 { "footprints", footprintCount },
3100 { "tracks", trackCount },
3101 { "vias", viaCount },
3102 { "zones", zoneCount }
3103 };
3104
3105 nlohmann::json warningsJson = nlohmann::json::array();
3106
3107 for( const wxString& warning : warnings )
3108 warningsJson.push_back( warning.ToStdString() );
3109
3110 report["warnings"] = warningsJson;
3111 report["errors"] = nlohmann::json::array();
3112
3113 wxString reportOutput = wxString::FromUTF8( report.dump( 2 ) );
3114
3115 if( !job->m_reportFile.IsEmpty() )
3116 {
3117 wxFile file( job->m_reportFile, wxFile::write );
3118
3119 if( file.IsOpened() )
3120 {
3121 file.Write( reportOutput );
3122 file.Close();
3123 }
3124 }
3125 else
3126 {
3127 m_reporter->Report( reportOutput + wxS( "\n" ), RPT_SEVERITY_INFO );
3128 }
3129 }
3131 {
3132 wxString text;
3133
3134 text += wxString::Format( wxS( "Import Report\n" ) );
3135 text += wxString::Format( wxS( "=============\n\n" ) );
3136 text += wxString::Format( wxS( "Source file: %s\n" ), inputFn.GetFullName() );
3137 text += wxString::Format( wxS( "Source format: %s\n" ), formatName );
3138 text += wxString::Format( wxS( "Output file: %s\n\n" ), outputFn.GetFullName() );
3139 text += wxS( "Statistics:\n" );
3140 text += wxString::Format( wxS( " Footprints: %zu\n" ), footprintCount );
3141 text += wxString::Format( wxS( " Tracks: %zu\n" ), trackCount );
3142 text += wxString::Format( wxS( " Vias: %zu\n" ), viaCount );
3143 text += wxString::Format( wxS( " Zones: %zu\n" ), zoneCount );
3144
3145 if( !warnings.empty() )
3146 {
3147 text += wxS( "\nWarnings:\n" );
3148
3149 for( const wxString& warning : warnings )
3150 text += wxString::Format( wxS( " - %s\n" ), warning );
3151 }
3152
3153 if( !job->m_reportFile.IsEmpty() )
3154 {
3155 wxFile file( job->m_reportFile, wxFile::write );
3156
3157 if( file.IsOpened() )
3158 {
3159 file.Write( text );
3160 file.Close();
3161 }
3162 }
3163 else
3164 {
3165 m_reporter->Report( text, RPT_SEVERITY_INFO );
3166 }
3167 }
3168 }
3169
3170 delete board;
3171 }
3172 catch( const IO_ERROR& ioe )
3173 {
3174 m_reporter->Report( wxString::Format( _( "Error during import: %s\n" ), ioe.What() ),
3176
3177 delete board;
3179 }
3180
3182}
@ 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:2778
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:2897
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:2758
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false, bool aPhysicalLayersOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition board.cpp:2348
void DeleteMARKERs()
Delete all MARKERS from the board.
Definition board.cpp:1737
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:1164
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:137
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