From ecfc8688329db626728f2ffafe0e45de9bbab1fa Mon Sep 17 00:00:00 2001 From: Marek Roszko Date: Thu, 9 Jan 2025 21:10:11 -0500 Subject: [PATCH] Combine/move svg plot job to single path --- common/jobs/job_export_pcb_svg.cpp | 7 +- common/jobs/job_export_pcb_svg.h | 4 +- kicad/cli/command_pcb_export_svg.cpp | 27 +++++--- pcbnew/dialogs/dialog_plot.cpp | 2 + pcbnew/pcb_plotter.cpp | 98 +++++++++++++++++++++------- pcbnew/pcb_plotter.h | 6 +- pcbnew/pcbnew_jobs_handler.cpp | 75 +++++++-------------- 7 files changed, 129 insertions(+), 90 deletions(-) diff --git a/common/jobs/job_export_pcb_svg.cpp b/common/jobs/job_export_pcb_svg.cpp index 8452209dd3..50440c8dda 100644 --- a/common/jobs/job_export_pcb_svg.cpp +++ b/common/jobs/job_export_pcb_svg.cpp @@ -24,15 +24,15 @@ NLOHMANN_JSON_SERIALIZE_ENUM( JOB_EXPORT_PCB_SVG::GEN_MODE, { - { JOB_EXPORT_PCB_SVG::GEN_MODE::DEPRECATED, "deprecated" }, - { JOB_EXPORT_PCB_SVG::GEN_MODE::NEW, "new" }, + { JOB_EXPORT_PCB_SVG::GEN_MODE::MULTI, "multi" }, // intended gui behavior, first as default + { JOB_EXPORT_PCB_SVG::GEN_MODE::SINGLE, "single" }, } ) JOB_EXPORT_PCB_SVG::JOB_EXPORT_PCB_SVG() : JOB_EXPORT_PCB_PLOT( JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::SVG, "svg", false ), m_pageSizeMode( 0 ), m_precision( 4 ), - m_genMode( GEN_MODE::NEW ) + m_genMode( GEN_MODE::SINGLE ) { m_plotDrawingSheet = true; @@ -48,7 +48,6 @@ JOB_EXPORT_PCB_SVG::JOB_EXPORT_PCB_SVG() : m_params.emplace_back( new JOB_PARAM( "drill_shape", &m_drillShapeOption, m_drillShapeOption ) ); m_params.emplace_back( new JOB_PARAM( "precision", &m_precision, m_precision ) ); - m_params.emplace_back( new JOB_PARAM( "gen_mode", &m_genMode, m_genMode ) ); } diff --git a/common/jobs/job_export_pcb_svg.h b/common/jobs/job_export_pcb_svg.h index 80d1b6f965..41582948ad 100644 --- a/common/jobs/job_export_pcb_svg.h +++ b/common/jobs/job_export_pcb_svg.h @@ -40,8 +40,8 @@ public: enum class GEN_MODE { - DEPRECATED, - NEW + SINGLE, + MULTI }; GEN_MODE m_genMode; diff --git a/kicad/cli/command_pcb_export_svg.cpp b/kicad/cli/command_pcb_export_svg.cpp index 404c991de3..a59b1918ba 100644 --- a/kicad/cli/command_pcb_export_svg.cpp +++ b/kicad/cli/command_pcb_export_svg.cpp @@ -32,7 +32,8 @@ #define ARG_EXCLUDE_DRAWING_SHEET "--exclude-drawing-sheet" #define ARG_PAGE_SIZE "--page-size-mode" -#define ARG_MODE_NEW "--mode-new" +#define ARG_MODE_SINGLE "--mode-single" +#define ARG_MODE_MULTI "--mode-multi" CLI::PCB_EXPORT_SVG_COMMAND::PCB_EXPORT_SVG_COMMAND() : PCB_EXPORT_BASE_COMMAND( "svg" ) { @@ -100,10 +101,18 @@ CLI::PCB_EXPORT_SVG_COMMAND::PCB_EXPORT_SVG_COMMAND() : PCB_EXPORT_BASE_COMMAND( "F.Cu,B.Cu" ) ) ) .metavar( "COMMON_LAYER_LIST" ); - m_argParser.add_argument( ARG_MODE_NEW ) + + m_argParser.add_argument( ARG_MODE_SINGLE ) .help( UTF8STDSTR( - _( "Opt into the new behavior which means output path is a directory, a file " - "per layer is generated and the common layers arg becomes available. " ) ) ) + _( "Generates a single file with the output arg path acting as the complete " + "directory and filename path. COMMON_LAYER_LIST does not function in this " + "mode. Instead LAYER_LIST controls all layers plotted." ) ) ) + .flag(); + + m_argParser.add_argument( ARG_MODE_MULTI ) + .help( UTF8STDSTR( _( "Generates one or more files with behavior similar to the KiCad " + "GUI plotting. The given output path specifies a directory in " + "which files may be output." ) ) ) .flag(); @@ -152,19 +161,19 @@ int CLI::PCB_EXPORT_SVG_COMMAND::doPerform( KIWAY& aKiway ) svgJob->m_printMaskLayer = m_selectedLayers; - if( m_argParser.get( ARG_MODE_NEW ) ) - svgJob->m_genMode = JOB_EXPORT_PCB_SVG::GEN_MODE::NEW; + if( m_argParser.get( ARG_MODE_MULTI ) ) + svgJob->m_genMode = JOB_EXPORT_PCB_SVG::GEN_MODE::MULTI; else - svgJob->m_genMode = JOB_EXPORT_PCB_SVG::GEN_MODE::DEPRECATED; + svgJob->m_genMode = JOB_EXPORT_PCB_SVG::GEN_MODE::SINGLE; - if( svgJob->m_genMode == JOB_EXPORT_PCB_SVG::GEN_MODE::DEPRECATED ) + if( svgJob->m_genMode == JOB_EXPORT_PCB_SVG::GEN_MODE::SINGLE ) { wxFprintf( stdout, wxT( "\033[33;1m%s\033[0m\n" ), _( "This command has deprecated behavior as of KiCad 9.0, the default behavior " "of this command will change in a future release." ) ); wxFprintf( stdout, wxT( "\033[33;1m%s\033[0m\n" ), - _( "The new behavior will match --mode-new" ) ); + _( "The new behavior will match --mode-multi" ) ); } int exitCode = aKiway.ProcessJob( KIWAY::FACE_PCB, svgJob.get() ); diff --git a/pcbnew/dialogs/dialog_plot.cpp b/pcbnew/dialogs/dialog_plot.cpp index 61585ba146..402acbad28 100644 --- a/pcbnew/dialogs/dialog_plot.cpp +++ b/pcbnew/dialogs/dialog_plot.cpp @@ -453,6 +453,7 @@ void DIALOG_PLOT::transferPlotParamsToJob() { JOB_EXPORT_PCB_SVG* svgJob = static_cast( m_job ); svgJob->m_precision = m_plotOpts.GetSvgPrecision(); + svgJob->m_genMode = JOB_EXPORT_PCB_SVG::GEN_MODE::MULTI; } if( m_job->m_plotFormat == JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::DXF ) @@ -463,6 +464,7 @@ void DIALOG_PLOT::transferPlotParamsToJob() : JOB_EXPORT_PCB_DXF::DXF_UNITS::MILLIMETERS; dxfJob->m_plotGraphicItemsUsingContours = m_plotOpts.GetPlotMode() == OUTLINE_MODE::SKETCH; dxfJob->m_polygonMode = m_plotOpts.GetDXFPlotPolygonMode(); + dxfJob->m_genMode = JOB_EXPORT_PCB_DXF::GEN_MODE::NEW; } if( m_job->m_plotFormat == JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::PDF ) diff --git a/pcbnew/pcb_plotter.cpp b/pcbnew/pcb_plotter.cpp index 53f8845c10..e02701693d 100644 --- a/pcbnew/pcb_plotter.cpp +++ b/pcbnew/pcb_plotter.cpp @@ -44,8 +44,14 @@ PCB_PLOTTER::PCB_PLOTTER( BOARD* aBoard, REPORTER* aReporter, PCB_PLOT_PARAMS& a } -bool PCB_PLOTTER::Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot, - const LSEQ& aCommonLayers, bool aUseGerberFileExtensions ) +bool PCB_PLOTTER::Plot( const wxString& aOutputPath, + const LSEQ& aLayersToPlot, + const LSEQ& aCommonLayers, + bool aUseGerberFileExtensions, + bool aOutputPathIsSingle, + std::optional aLayerName, + std::optional aSheetName, + std::optional aSheetPath ) { std::function textResolver = [&]( wxString* token ) -> bool { @@ -53,10 +59,34 @@ bool PCB_PLOTTER::Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot, return m_board->ResolveTextVar( token, 0 ); }; + // sanity, ensure one layer to print + if( aLayersToPlot.size() < 1 ) + { + m_reporter->Report( _( "No layers selected for plotting." ), RPT_SEVERITY_ERROR ); + return false; + } + + // To reuse logic, in single plot mode, we want to kick any extra layers from the main list to commonLayers + LSEQ layersToPlot; + LSEQ commonLayers; + if( aOutputPathIsSingle ) + { + layersToPlot.push_back( aLayersToPlot[0] ); + + if( aLayersToPlot.size() > 1 ) + commonLayers.insert( commonLayers.end(), aLayersToPlot.begin() + 1, + aLayersToPlot.end() ); + } + else + { + layersToPlot = aLayersToPlot; + commonLayers = aCommonLayers; + } + size_t finalPageCount = 0; - for( size_t i = 0; i < aLayersToPlot.size(); i++ ) + for( size_t i = 0; i < layersToPlot.size(); i++ ) { - PCB_LAYER_ID layer = aLayersToPlot[i]; + PCB_LAYER_ID layer = layersToPlot[i]; if( copperLayerShouldBeSkipped( layer ) ) continue; @@ -65,45 +95,53 @@ bool PCB_PLOTTER::Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot, std::unique_ptr jobfile_writer; - if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER ) + if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && !aOutputPathIsSingle ) { jobfile_writer = std::make_unique( m_board, m_reporter ); } wxString fileExt( GetDefaultPlotExtension( m_plotOpts.GetFormat() ) ); wxString sheetPath; + wxString msg; PLOTTER* plotter = nullptr; - for( size_t i = 0, pageNum = 1; i < aLayersToPlot.size(); i++ ) + for( size_t i = 0, pageNum = 1; i < layersToPlot.size(); i++ ) { - PCB_LAYER_ID layer = aLayersToPlot[i]; + PCB_LAYER_ID layer = layersToPlot[i]; if( copperLayerShouldBeSkipped( layer ) ) continue; - LSEQ plotSequence = getPlotSequence( layer, aCommonLayers ); + LSEQ plotSequence = getPlotSequence( layer, commonLayers ); wxString layerName = m_board->GetLayerName( layer ); wxFileName fn; - wxFileName brdFn = m_board->GetFileName(); - wxString msg; - fn.Assign( aOutputPath, brdFn.GetName() ); - // Use Gerber Extensions based on layer number - // (See http://en.wikipedia.org/wiki/Gerber_File) - if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && aUseGerberFileExtensions ) - fileExt = GetGerberProtelExtension( layer ); - - if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF && m_plotOpts.m_PDFSingle ) + if( aOutputPathIsSingle ) { - fn.SetExt( GetDefaultPlotExtension( PLOT_FORMAT::PDF ) ); + fn = wxFileName( aOutputPath ); } else { - BuildPlotFileName( &fn, aOutputPath, layerName, fileExt ); + wxFileName brdFn = m_board->GetFileName(); + fn.Assign( aOutputPath, brdFn.GetName() ); + + // Use Gerber Extensions based on layer number + // (See http://en.wikipedia.org/wiki/Gerber_File) + if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && aUseGerberFileExtensions ) + fileExt = GetGerberProtelExtension( layer ); + + if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF && m_plotOpts.m_PDFSingle ) + { + fn.SetExt( GetDefaultPlotExtension( PLOT_FORMAT::PDF ) ); + } + else + { + BuildPlotFileName( &fn, aOutputPath, layerName, fileExt ); + } } - if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER ) + if( jobfile_writer ) { wxString fullname = fn.GetFullName(); jobfile_writer->AddGbrFile( layer, fullname ); @@ -119,6 +157,18 @@ bool PCB_PLOTTER::Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot, wxString pageName = layerName; wxString sheetName = layerName; + if( aLayerName.has_value() ) + { + layerName = aLayerName.value(); + pageName = aLayerName.value(); + } + + if( aSheetName.has_value() ) + sheetName = aSheetName.value(); + + if( aSheetPath.has_value() ) + sheetPath = aSheetPath.value(); + plotter = StartPlotBoard( m_board, &m_plotOpts, layer, layerName, fn.GetFullPath(), sheetName, sheetPath, pageName, pageNumber, finalPageCount ); @@ -146,7 +196,7 @@ bool PCB_PLOTTER::Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot, if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF && m_plotOpts.m_PDFSingle - && i != aLayersToPlot.size() - 1 ) + && i != layersToPlot.size() - 1 ) { wxString pageNumber = wxString::Format( "%zu", pageNum + 1 ); @@ -156,9 +206,9 @@ bool PCB_PLOTTER::Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot, do { ++nextI; - nextLayer = aLayersToPlot[nextI]; + nextLayer = layersToPlot[nextI]; } while( copperLayerShouldBeSkipped( nextLayer ) - && ( nextI < aLayersToPlot.size() - 1 ) ); + && ( nextI < layersToPlot.size() - 1 ) ); wxString pageName = m_board->GetLayerName( nextLayer ); wxString sheetName = layerName; @@ -202,7 +252,7 @@ bool PCB_PLOTTER::Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot, wxSafeYield(); // displays report message. } - if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && m_plotOpts.GetCreateGerberJobFile() ) + if( jobfile_writer && m_plotOpts.GetCreateGerberJobFile() ) { // Pick the basename from the board file wxFileName fn( m_board->GetFileName() ); diff --git a/pcbnew/pcb_plotter.h b/pcbnew/pcb_plotter.h index 0b1274108d..77e8b8a7a7 100644 --- a/pcbnew/pcb_plotter.h +++ b/pcbnew/pcb_plotter.h @@ -36,7 +36,11 @@ public: PCB_PLOTTER( BOARD* aBoard, REPORTER* aReporter, PCB_PLOT_PARAMS& aParams ); bool Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot, const LSEQ& aCommonLayers, - bool aUseGerberFileExtensions ); + bool aUseGerberFileExtensions, + bool aOutputPathIsSingle = false, + std::optional aLayerName = std::nullopt, + std::optional aSheetName = std::nullopt, + std::optional aSheetPath = std::nullopt ); /** * All copper layers that are disabled are actually selected diff --git a/pcbnew/pcbnew_jobs_handler.cpp b/pcbnew/pcbnew_jobs_handler.cpp index 036ab962fa..a228c75c40 100644 --- a/pcbnew/pcbnew_jobs_handler.cpp +++ b/pcbnew/pcbnew_jobs_handler.cpp @@ -702,60 +702,35 @@ int PCBNEW_JOBS_HANDLER::JobExportSvg( JOB* aJob ) brd->GetProject()->ApplyTextVars( aJob->GetVarOverrides() ); brd->SynchronizeProperties(); - if( aSvgJob->m_genMode == JOB_EXPORT_PCB_SVG::GEN_MODE::DEPRECATED ) + PCB_PLOT_PARAMS plotOpts; + PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aSvgJob ); + + PCB_PLOTTER plotter( brd, m_reporter, plotOpts ); + + std::optional layerName; + std::optional sheetName; + std::optional sheetPath; + + if( aSvgJob->m_genMode == JOB_EXPORT_PCB_SVG::GEN_MODE::SINGLE ) { - PCB_PLOT_SVG_OPTIONS svgPlotOptions; - svgPlotOptions.m_blackAndWhite = aSvgJob->m_blackAndWhite; - svgPlotOptions.m_colorTheme = aSvgJob->m_colorTheme; - svgPlotOptions.m_outputFile = aSvgJob->GetFullOutputPath(); - svgPlotOptions.m_mirror = aSvgJob->m_mirror; - svgPlotOptions.m_negative = aSvgJob->m_negative; - svgPlotOptions.m_pageSizeMode = aSvgJob->m_pageSizeMode; - svgPlotOptions.m_printMaskLayer = aSvgJob->m_printMaskLayer; - svgPlotOptions.m_plotFrame = aSvgJob->m_plotDrawingSheet; - svgPlotOptions.m_sketchPadsOnFabLayers = aSvgJob->m_sketchPadsOnFabLayers; - svgPlotOptions.m_hideDNPFPsOnFabLayers = aSvgJob->m_hideDNPFPsOnFabLayers; - svgPlotOptions.m_sketchDNPFPsOnFabLayers = aSvgJob->m_sketchDNPFPsOnFabLayers; - svgPlotOptions.m_crossoutDNPFPsOnFabLayers = aSvgJob->m_crossoutDNPFPsOnFabLayers; - svgPlotOptions.m_precision = aSvgJob->m_precision; - - switch( aSvgJob->m_drillShapeOption ) - { - default: - case JOB_EXPORT_PCB_PLOT::DRILL_MARKS::NO_DRILL_SHAPE: - svgPlotOptions.m_drillShapeOption = static_cast( DRILL_MARKS::NO_DRILL_SHAPE ); - break; - case JOB_EXPORT_PCB_PLOT::DRILL_MARKS::SMALL_DRILL_SHAPE: - svgPlotOptions.m_drillShapeOption = static_cast( DRILL_MARKS::SMALL_DRILL_SHAPE ); - break; - case JOB_EXPORT_PCB_PLOT::DRILL_MARKS::FULL_DRILL_SHAPE: - svgPlotOptions.m_drillShapeOption = static_cast( DRILL_MARKS::FULL_DRILL_SHAPE ); - break; - } + if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) ) + layerName = aSvgJob->GetVarOverrides().at( wxT( "LAYER" ) ); - if( EXPORT_SVG::Plot( brd, svgPlotOptions ) ) - { - m_reporter->Report( _( "Successfully created svg file" ) + wxS( "\n" ), - RPT_SEVERITY_INFO ); - return CLI::EXIT_CODES::OK; - } - else - { - m_reporter->Report( _( "Error creating svg file" ) + wxS( "\n" ), RPT_SEVERITY_ERROR ); - return CLI::EXIT_CODES::ERR_INVALID_OUTPUT_CONFLICT; - } + if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) ) + sheetName = aSvgJob->GetVarOverrides().at( wxT( "SHEETNAME" ) ); + + if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) ) + sheetPath = aSvgJob->GetVarOverrides().at( wxT( "SHEETPATH" ) ); } - else - { - PCB_PLOT_PARAMS plotOpts; - PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aSvgJob ); - PCB_PLOTTER plotter( brd, m_reporter, plotOpts ); - if( !plotter.Plot( aSvgJob->GetFullOutputPath(), aSvgJob->m_printMaskLayer, - aSvgJob->m_printMaskLayersToIncludeOnAllLayers, false ) ) - { - return CLI::EXIT_CODES::ERR_UNKNOWN; - } + if( !plotter.Plot( aSvgJob->GetFullOutputPath(), aSvgJob->m_printMaskLayer, + aSvgJob->m_printMaskLayersToIncludeOnAllLayers, false, + aSvgJob->m_genMode == JOB_EXPORT_PCB_SVG::GEN_MODE::SINGLE, + layerName, + sheetName, + sheetPath ) ) + { + return CLI::EXIT_CODES::ERR_UNKNOWN; } return CLI::EXIT_CODES::OK;