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