7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-19 22:51:40 +00:00

Combine/move svg plot job to single path

This commit is contained in:
Marek Roszko 2025-01-09 21:10:11 -05:00
parent 4dfcbc6d2a
commit ecfc868832
7 changed files with 129 additions and 90 deletions

View File

@ -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_MARKS>( "drill_shape", &m_drillShapeOption, m_drillShapeOption ) );
m_params.emplace_back( new JOB_PARAM<unsigned int>( "precision", &m_precision, m_precision ) );
m_params.emplace_back( new JOB_PARAM<GEN_MODE>( "gen_mode", &m_genMode, m_genMode ) );
}

View File

@ -40,8 +40,8 @@ public:
enum class GEN_MODE
{
DEPRECATED,
NEW
SINGLE,
MULTI
};
GEN_MODE m_genMode;

View File

@ -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<bool>( ARG_MODE_NEW ) )
svgJob->m_genMode = JOB_EXPORT_PCB_SVG::GEN_MODE::NEW;
if( m_argParser.get<bool>( 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() );

View File

@ -453,6 +453,7 @@ void DIALOG_PLOT::transferPlotParamsToJob()
{
JOB_EXPORT_PCB_SVG* svgJob = static_cast<JOB_EXPORT_PCB_SVG*>( 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 )

View File

@ -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<wxString> aLayerName,
std::optional<wxString> aSheetName,
std::optional<wxString> aSheetPath )
{
std::function<bool( wxString* )> textResolver = [&]( wxString* token ) -> bool
{
@ -53,10 +59,34 @@ bool PCB_PLOTTER::Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot,
return m_board->ResolveTextVar( token, 0 );
};
size_t finalPageCount = 0;
for( size_t i = 0; i < aLayersToPlot.size(); i++ )
// sanity, ensure one layer to print
if( aLayersToPlot.size() < 1 )
{
PCB_LAYER_ID layer = aLayersToPlot[i];
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 < layersToPlot.size(); 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<GERBER_JOBFILE_WRITER> jobfile_writer;
if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER )
if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && !aOutputPathIsSingle )
{
jobfile_writer = std::make_unique<GERBER_JOBFILE_WRITER>( 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() );

View File

@ -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<wxString> aLayerName = std::nullopt,
std::optional<wxString> aSheetName = std::nullopt,
std::optional<wxString> aSheetPath = std::nullopt );
/**
* All copper layers that are disabled are actually selected

View File

@ -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<wxString> layerName;
std::optional<wxString> sheetName;
std::optional<wxString> 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;
if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
layerName = aSvgJob->GetVarOverrides().at( wxT( "LAYER" ) );
switch( aSvgJob->m_drillShapeOption )
{
default:
case JOB_EXPORT_PCB_PLOT::DRILL_MARKS::NO_DRILL_SHAPE:
svgPlotOptions.m_drillShapeOption = static_cast<int>( DRILL_MARKS::NO_DRILL_SHAPE );
break;
case JOB_EXPORT_PCB_PLOT::DRILL_MARKS::SMALL_DRILL_SHAPE:
svgPlotOptions.m_drillShapeOption = static_cast<int>( DRILL_MARKS::SMALL_DRILL_SHAPE );
break;
case JOB_EXPORT_PCB_PLOT::DRILL_MARKS::FULL_DRILL_SHAPE:
svgPlotOptions.m_drillShapeOption = static_cast<int>( DRILL_MARKS::FULL_DRILL_SHAPE );
break;
}
if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
sheetName = aSvgJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
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( "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;