From a590d28c8e0ab7b5f45da95b6ff8a2233ed26a14 Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Wed, 23 Apr 2025 13:41:00 +0000 Subject: [PATCH] ADDED: Postscript job support --- common/CMakeLists.txt | 1 + common/jobs/job_export_pcb_ps.cpp | 66 ++++++++++ common/jobs/job_export_pcb_ps.h | 53 ++++++++ kicad/CMakeLists.txt | 1 + kicad/cli/command_pcb_export_ps.cpp | 185 ++++++++++++++++++++++++++++ kicad/cli/command_pcb_export_ps.h | 38 ++++++ kicad/kicad_cli.cpp | 3 + pcbnew/dialogs/dialog_plot.cpp | 63 ++++++++-- pcbnew/pcb_plotter.cpp | 11 ++ pcbnew/pcbnew_jobs_handler.cpp | 98 +++++++++++++++ pcbnew/pcbnew_jobs_handler.h | 1 + 11 files changed, 512 insertions(+), 8 deletions(-) create mode 100644 common/jobs/job_export_pcb_ps.cpp create mode 100644 common/jobs/job_export_pcb_ps.h create mode 100644 kicad/cli/command_pcb_export_ps.cpp create mode 100644 kicad/cli/command_pcb_export_ps.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a7e63c1d96..2583cd95e9 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -77,6 +77,7 @@ set( KICOMMON_SRCS jobs/job_export_pcb_pdf.cpp jobs/job_export_pcb_plot.cpp jobs/job_export_pcb_pos.cpp + jobs/job_export_pcb_ps.cpp jobs/job_export_pcb_svg.cpp jobs/job_export_pcb_gencad.cpp jobs/job_export_pcb_3d.cpp diff --git a/common/jobs/job_export_pcb_ps.cpp b/common/jobs/job_export_pcb_ps.cpp new file mode 100644 index 0000000000..d0e650cce5 --- /dev/null +++ b/common/jobs/job_export_pcb_ps.cpp @@ -0,0 +1,66 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 Mark Roszko + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include + + +NLOHMANN_JSON_SERIALIZE_ENUM( JOB_EXPORT_PCB_PS::GEN_MODE, + { + { JOB_EXPORT_PCB_PS::GEN_MODE::MULTI, "multi" }, + { JOB_EXPORT_PCB_PS::GEN_MODE::SINGLE, "single" }, + } ) + + +JOB_EXPORT_PCB_PS::JOB_EXPORT_PCB_PS() : + JOB_EXPORT_PCB_PLOT( JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::POST, "ps", false ), + m_genMode( GEN_MODE::MULTI ), m_trackWidthCorrection( 0.0 ), + m_XScaleAdjust( 1.0 ), m_YScaleAdjust( 1.0 ), m_forceA4( false ), + m_useGlobalSettings( true ) +{ + m_plotDrawingSheet = false; + + m_params.emplace_back( new JOB_PARAM( "color_theme", + &m_colorTheme, m_colorTheme ) ); + m_params.emplace_back( new JOB_PARAM( "gen_mode", &m_genMode, m_genMode ) ); + m_params.emplace_back( new JOB_PARAM( "track_width_correction", + &m_trackWidthCorrection, m_trackWidthCorrection ) ); + m_params.emplace_back( new JOB_PARAM( "x_scale_factor", &m_XScaleAdjust, m_XScaleAdjust) ); + m_params.emplace_back( new JOB_PARAM( "y_scale_factor", &m_YScaleAdjust, m_YScaleAdjust) ); + m_params.emplace_back( new JOB_PARAM( "force_a4", &m_forceA4, m_forceA4) ); + m_params.emplace_back( new JOB_PARAM( "use_global_settings", + &m_useGlobalSettings, m_useGlobalSettings) ); +} + + +wxString JOB_EXPORT_PCB_PS::GetDefaultDescription() const +{ + return wxString::Format( _( "Export Postscript" ) ); +} + + +wxString JOB_EXPORT_PCB_PS::GetSettingsDialogTitle() const +{ + return wxString::Format( _( "Export Postscript Job Settings" ) ); +} + + +REGISTER_JOB( pcb_export_ps, _HKI( "PCB: Export Postscript" ), KIWAY::FACE_PCB, JOB_EXPORT_PCB_PS ); diff --git a/common/jobs/job_export_pcb_ps.h b/common/jobs/job_export_pcb_ps.h new file mode 100644 index 0000000000..eea5b4e42f --- /dev/null +++ b/common/jobs/job_export_pcb_ps.h @@ -0,0 +1,53 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mark Roszko + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef JOB_EXPORT_PCB_PS_H +#define JOB_EXPORT_PCB_PS_H + +#include +#include +#include +#include +#include +#include + + +class KICOMMON_API JOB_EXPORT_PCB_PS : public JOB_EXPORT_PCB_PLOT +{ +public: + JOB_EXPORT_PCB_PS(); + wxString GetDefaultDescription() const override; + wxString GetSettingsDialogTitle() const override; + + enum class GEN_MODE + { + SINGLE, + MULTI + }; + + GEN_MODE m_genMode; + double m_trackWidthCorrection; + double m_XScaleAdjust; + double m_YScaleAdjust; + bool m_forceA4; + bool m_useGlobalSettings; +}; + +#endif diff --git a/kicad/CMakeLists.txt b/kicad/CMakeLists.txt index d1dcdd2084..3bad630103 100644 --- a/kicad/CMakeLists.txt +++ b/kicad/CMakeLists.txt @@ -66,6 +66,7 @@ set( KICAD_CLI_SRCS cli/command_pcb_export_odb.cpp cli/command_pcb_export_pdf.cpp cli/command_pcb_export_pos.cpp + cli/command_pcb_export_ps.cpp cli/command_pcb_export_svg.cpp cli/command_fp_export_svg.cpp cli/command_fp_upgrade.cpp diff --git a/kicad/cli/command_pcb_export_ps.cpp b/kicad/cli/command_pcb_export_ps.cpp new file mode 100644 index 0000000000..9ca31d5413 --- /dev/null +++ b/kicad/cli/command_pcb_export_ps.cpp @@ -0,0 +1,185 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mark Roszko + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include "command_pcb_export_ps.h" +#include +#include "jobs/job_export_pcb_ps.h" +#include +#include +#include + +#include + +#define ARG_MODE_SINGLE "--mode-single" +#define ARG_MODE_MULTI "--mode-multi" +#define ARG_TRACK_WIDTH_CORRECTION "--track-width-correction" +#define ARG_X_SCALE_FACTOR "--x-scale-factor" +#define ARG_Y_SCALE_FACTOR "--y-scale-factor" +#define ARG_FORCE_A4 "--force-a4" + +CLI::PCB_EXPORT_PS_COMMAND::PCB_EXPORT_PS_COMMAND() : + PCB_EXPORT_BASE_COMMAND( "ps", false, true ) +{ + m_argParser.add_description( UTF8STDSTR( _( "Generate Postscript from a list of layers" ) ) ); + + addLayerArg(); + addCommonLayersArg(); + addDrawingSheetArg(); + addDefineArg(); + + m_argParser.add_argument( "-m", ARG_MIRROR ) + .help( UTF8STDSTR( _( "Mirror the board (useful for trying to show bottom layers)" ) ) ) + .flag(); + + m_argParser.add_argument( "--erd", ARG_EXCLUDE_REFDES ) + .help( UTF8STDSTR( _( "Exclude the reference designator text" ) ) ) + .flag(); + + m_argParser.add_argument( "--ev", ARG_EXCLUDE_VALUE ) + .help( UTF8STDSTR( _( "Exclude the value text" ) ) ) + .flag(); + + m_argParser.add_argument( "--ibt", ARG_INCLUDE_BORDER_TITLE ) + .help( UTF8STDSTR( _( "Include the border and title block" ) ) ) + .flag(); + + m_argParser.add_argument( ARG_SUBTRACT_SOLDERMASK ) + .help( UTF8STDSTR( _( "Subtract soldermask from silkscreen" ) ) ) + .flag(); + + m_argParser.add_argument( "--sp", ARG_SKETCH_PADS_ON_FAB_LAYERS ) + .help( UTF8STDSTR( _( ARG_SKETCH_PADS_ON_FAB_LAYERS_DESC ) ) ) + .flag(); + + m_argParser.add_argument( "--hdnp", ARG_HIDE_DNP_FPS_ON_FAB_LAYERS ) + .help( UTF8STDSTR( _( ARG_HIDE_DNP_FPS_ON_FAB_LAYERS_DESC ) ) ) + .flag(); + + m_argParser.add_argument( "--sdnp", ARG_SKETCH_DNP_FPS_ON_FAB_LAYERS ) + .help( UTF8STDSTR( _( ARG_SKETCH_DNP_FPS_ON_FAB_LAYERS_DESC ) ) ) + .flag(); + + m_argParser.add_argument( "--cdnp", ARG_CROSSOUT_DNP_FPS_ON_FAB_LAYERS ) + .help( UTF8STDSTR( _( ARG_CROSSOUT_DNP_FPS_ON_FAB_LAYERS_DESC ) ) ) + .flag(); + + m_argParser.add_argument( ARG_NEGATIVE_SHORT, ARG_NEGATIVE ) + .help( UTF8STDSTR( _( ARG_NEGATIVE_DESC ) ) ) + .flag(); + + m_argParser.add_argument( ARG_BLACKANDWHITE ) + .help( UTF8STDSTR( _( ARG_BLACKANDWHITE_DESC ) ) ) + .flag(); + + m_argParser.add_argument( "-t", ARG_THEME ) + .default_value( std::string() ) + .help( UTF8STDSTR( _( "Color theme to use (will default to PCB Editor settings)" ) ) ) + .metavar( "THEME_NAME" ); + + m_argParser.add_argument( ARG_DRILL_SHAPE_OPTION ) + .help( UTF8STDSTR( _( ARG_DRILL_SHAPE_OPTION_DESC ) ) ) + .scan<'i', int>() + .default_value( 2 ); + + m_argParser.add_argument( ARG_MODE_SINGLE ) + .help( UTF8STDSTR( + _( "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(); + + m_argParser.add_argument( "-C", ARG_TRACK_WIDTH_CORRECTION ) + .help( UTF8STDSTR( _( "Track width correction [mm]. Used to compensate errors in " + "track widths, pad and via sizes." ) ) ) + .scan<'g', double>() + .default_value( 0 ) + .metavar( "TRACK_COR" ); + + m_argParser.add_argument( "-X", ARG_X_SCALE_FACTOR ) + .help( UTF8STDSTR( _( "X scale adjust for exact scale." ) ) ) + .scan<'g', double>() + .default_value( 1.0 ) + .metavar( "X_SCALE" ); + + m_argParser.add_argument( "-Y", ARG_Y_SCALE_FACTOR ) + .help( UTF8STDSTR( _( "Y scale adjust for exact scale." ) ) ) + .scan<'g', double>() + .default_value( 1.0 ) + .metavar( "Y_SCALE" ); + + m_argParser.add_argument( "-A", ARG_FORCE_A4 ) + .help( UTF8STDSTR( _( "Force A4 paper size." ) ) ) + .flag(); +} + + +int CLI::PCB_EXPORT_PS_COMMAND::doPerform( KIWAY& aKiway ) +{ + std::unique_ptr psJob( new JOB_EXPORT_PCB_PS() ); + + psJob->m_filename = m_argInput; + psJob->SetConfiguredOutputPath( m_argOutput ); + psJob->m_drawingSheet = m_argDrawingSheet; + psJob->SetVarOverrides( m_argDefineVars ); + + if( !wxFile::Exists( psJob->m_filename ) ) + { + wxFprintf( stderr, _( "Board file does not exist or is not accessible\n" ) ); + return EXIT_CODES::ERR_INVALID_INPUT_FILE; + } + + psJob->m_plotFootprintValues = !m_argParser.get( ARG_EXCLUDE_VALUE ); + psJob->m_plotRefDes = !m_argParser.get( ARG_EXCLUDE_REFDES ); + + psJob->m_plotDrawingSheet = m_argParser.get( ARG_INCLUDE_BORDER_TITLE ); + psJob->m_subtractSolderMaskFromSilk = m_argParser.get( ARG_SUBTRACT_SOLDERMASK ); + psJob->m_mirror = m_argParser.get( ARG_MIRROR ); + psJob->m_blackAndWhite = m_argParser.get( ARG_BLACKANDWHITE ); + psJob->m_colorTheme = From_UTF8( m_argParser.get( ARG_THEME ).c_str() ); + psJob->m_negative = m_argParser.get( ARG_NEGATIVE ); + + psJob->m_sketchPadsOnFabLayers = m_argParser.get( ARG_SKETCH_PADS_ON_FAB_LAYERS ); + + int drillShape = m_argParser.get( ARG_DRILL_SHAPE_OPTION ); + psJob->m_drillShapeOption = static_cast( drillShape ); + + psJob->m_argLayers = From_UTF8( m_argParser.get( ARG_LAYERS ).c_str() ); + psJob->m_argCommonLayers = From_UTF8( m_argParser.get( ARG_COMMON_LAYERS ).c_str() ); + + if( m_argParser.get( ARG_MODE_MULTI ) ) + psJob->m_genMode = JOB_EXPORT_PCB_PS::GEN_MODE::MULTI; + else if( m_argParser.get( ARG_MODE_SINGLE ) ) + psJob->m_genMode = JOB_EXPORT_PCB_PS::GEN_MODE::SINGLE; + + psJob->m_trackWidthCorrection = m_argParser.get( ARG_TRACK_WIDTH_CORRECTION ); + psJob->m_XScaleAdjust = m_argParser.get( ARG_X_SCALE_FACTOR ); + psJob->m_YScaleAdjust = m_argParser.get( ARG_Y_SCALE_FACTOR ); + psJob->m_forceA4 = m_argParser.get( ARG_FORCE_A4 ); + + LOCALE_IO dummy; // Switch to "C" locale + return aKiway.ProcessJob( KIWAY::FACE_PCB, psJob.get() ); +} diff --git a/kicad/cli/command_pcb_export_ps.h b/kicad/cli/command_pcb_export_ps.h new file mode 100644 index 0000000000..313e93691c --- /dev/null +++ b/kicad/cli/command_pcb_export_ps.h @@ -0,0 +1,38 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mark Roszko + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef COMMAND_EXPORT_PCB_PS_H +#define COMMAND_EXPORT_PCB_PS_H + +#include "command_pcb_export_base.h" + +namespace CLI +{ +class PCB_EXPORT_PS_COMMAND : public PCB_EXPORT_BASE_COMMAND +{ +public: + PCB_EXPORT_PS_COMMAND(); + +protected: + int doPerform( KIWAY& aKiway ) override; +}; +} // namespace CLI + +#endif \ No newline at end of file diff --git a/kicad/kicad_cli.cpp b/kicad/kicad_cli.cpp index 883458aa2d..19730eb7ea 100644 --- a/kicad/kicad_cli.cpp +++ b/kicad/kicad_cli.cpp @@ -63,6 +63,7 @@ #include "cli/command_pcb_export_odb.h" #include "cli/command_pcb_export_pdf.h" #include "cli/command_pcb_export_pos.h" +#include "cli/command_pcb_export_ps.h" #include "cli/command_pcb_export_svg.h" #include "cli/command_sch_export_bom.h" #include "cli/command_sch_export_pythonbom.h" @@ -127,6 +128,7 @@ static CLI::PCB_EXPORT_3D_COMMAND exportPcbStlCmd{ "stl", UTF8STDSTR( _( static CLI::PCB_EXPORT_SVG_COMMAND exportPcbSvgCmd{}; static CLI::PCB_EXPORT_PDF_COMMAND exportPcbPdfCmd{}; static CLI::PCB_EXPORT_POS_COMMAND exportPcbPosCmd{}; +static CLI::PCB_EXPORT_PS_COMMAND exportPcbPsCmd{}; static CLI::PCB_EXPORT_GERBER_COMMAND exportPcbGerberCmd{}; static CLI::PCB_EXPORT_GERBERS_COMMAND exportPcbGerbersCmd{}; static CLI::PCB_EXPORT_GENCAD_COMMAND exportPcbGencadCmd{}; @@ -204,6 +206,7 @@ static std::vector commandStack = { &exportPcbOdbCmd, &exportPcbPdfCmd, &exportPcbPosCmd, + &exportPcbPsCmd, &exportPcbStepCmd, &exportPcbSvgCmd, &exportPcbVrmlCmd, diff --git a/pcbnew/dialogs/dialog_plot.cpp b/pcbnew/dialogs/dialog_plot.cpp index 83a435c45b..12ee9bf120 100644 --- a/pcbnew/dialogs/dialog_plot.cpp +++ b/pcbnew/dialogs/dialog_plot.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -272,16 +273,30 @@ void DIALOG_PLOT::init_Dialog() if( !m_job && !projectFile.m_PcbLastPath[ LAST_PATH_PLOT ].IsEmpty() ) m_plotOpts.SetOutputDirectory( projectFile.m_PcbLastPath[ LAST_PATH_PLOT ] ); - m_XScaleAdjust = cfg->m_Plot.fine_scale_x; - m_YScaleAdjust = cfg->m_Plot.fine_scale_y; + if( m_job + && !( m_job->m_plotFormat == JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::POST + && dynamic_cast( m_job )->m_useGlobalSettings ) ) + { + // When we are using a job we get the PS adjust values from the plot options + // The exception is when this is a fresh job and we want to get the global values as defaults + m_XScaleAdjust = m_plotOpts.GetFineScaleAdjustX(); + m_YScaleAdjust = m_plotOpts.GetFineScaleAdjustY(); + m_PSWidthAdjust = m_plotOpts.GetWidthAdjust(); + } + else + { + // The default is to use the global adjusts from the pcbnew settings + m_XScaleAdjust = cfg->m_Plot.fine_scale_x; + m_YScaleAdjust = cfg->m_Plot.fine_scale_y; + // m_PSWidthAdjust is stored in mm in user config + m_PSWidthAdjust = KiROUND( cfg->m_Plot.ps_fine_width_adjust * pcbIUScale.IU_PER_MM ); + } m_zoneFillCheck->SetValue( cfg->m_Plot.check_zones_before_plotting ); m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) ); m_openDirButton->SetBitmap( KiBitmapBundle( BITMAPS::small_new_window ) ); - // m_PSWidthAdjust is stored in mm in user config - m_PSWidthAdjust = KiROUND( cfg->m_Plot.ps_fine_width_adjust * pcbIUScale.IU_PER_MM ); // The reasonable width correction value must be in a range of // [-(MinTrackWidth-1), +(MinClearanceValue-1)] decimils. @@ -458,6 +473,19 @@ void DIALOG_PLOT::transferPlotParamsToJob() dxfJob->m_genMode = JOB_EXPORT_PCB_DXF::GEN_MODE::MULTI; } + if( m_job->m_plotFormat == JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::POST ) + { + JOB_EXPORT_PCB_PS* psJob = static_cast( m_job ); + psJob->m_genMode = JOB_EXPORT_PCB_PS::GEN_MODE::MULTI; + psJob->m_XScaleAdjust = m_plotOpts.GetFineScaleAdjustX(); + psJob->m_YScaleAdjust = m_plotOpts.GetFineScaleAdjustY(); + psJob->m_trackWidthCorrection = pcbIUScale.IUTomm( m_plotOpts.GetWidthAdjust() ); + psJob->m_forceA4 = m_plotOpts.GetA4Output(); + // For a fresh job we got the adjusts from the global pcbnew settings + // After the user confirmed and/or changed them we stop using the global adjusts + psJob->m_useGlobalSettings = false; + } + if( m_job->m_plotFormat == JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::PDF ) { JOB_EXPORT_PCB_PDF* pdfJob = static_cast( m_job ); @@ -1120,8 +1148,18 @@ void DIALOG_PLOT::applyPlotSettings() auto cfg = m_editFrame->GetPcbNewSettings(); - cfg->m_Plot.fine_scale_x = m_XScaleAdjust; - cfg->m_Plot.fine_scale_y = m_YScaleAdjust; + if( m_job ) + { + // When using a job we store the adjusts in the plot options + tempOptions.SetFineScaleAdjustX( m_XScaleAdjust ); + tempOptions.SetFineScaleAdjustY( m_YScaleAdjust ); + } + else + { + // The default is to use the pcbnew settings, so here we modify them + cfg->m_Plot.fine_scale_x = m_XScaleAdjust; + cfg->m_Plot.fine_scale_y = m_YScaleAdjust; + } cfg->m_Plot.check_zones_before_plotting = m_zoneFillCheck->GetValue(); @@ -1137,8 +1175,17 @@ void DIALOG_PLOT::applyPlotSettings() reporter.Report( msg, RPT_SEVERITY_WARNING ); } - // Store m_PSWidthAdjust in mm in user config - cfg->m_Plot.ps_fine_width_adjust = pcbIUScale.IUTomm( m_PSWidthAdjust ); + if( m_job ) + { + // When using a job we store the adjusts in the plot options + tempOptions.SetWidthAdjust( m_PSWidthAdjust ); + } + else + { + // The default is to use the pcbnew settings, so here we modify them + // Store m_PSWidthAdjust in mm in user config + cfg->m_Plot.ps_fine_width_adjust = pcbIUScale.IUTomm( m_PSWidthAdjust ); + } tempOptions.SetFormat( getPlotFormat() ); diff --git a/pcbnew/pcb_plotter.cpp b/pcbnew/pcb_plotter.cpp index 0a6a8afe7d..216ba0fb98 100644 --- a/pcbnew/pcb_plotter.cpp +++ b/pcbnew/pcb_plotter.cpp @@ -33,9 +33,11 @@ #include #include #include +#include #include #include #include +#include // for KiROUND PCB_PLOTTER::PCB_PLOTTER( BOARD* aBoard, REPORTER* aReporter, PCB_PLOT_PARAMS& aParams ) : @@ -403,6 +405,15 @@ void PCB_PLOTTER::PlotJobToPlotOpts( PCB_PLOT_PARAMS& aOpts, JOB_EXPORT_PCB_PLOT aOpts.m_PDFSingle = pdfJob->m_pdfSingle; } + if( aJob->m_plotFormat == JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::POST ) + { + JOB_EXPORT_PCB_PS* psJob = static_cast( aJob ); + aOpts.SetWidthAdjust( KiROUND( psJob->m_trackWidthCorrection * pcbIUScale.IU_PER_MM ) ); + aOpts.SetFineScaleAdjustX( psJob->m_XScaleAdjust ); + aOpts.SetFineScaleAdjustY( psJob->m_YScaleAdjust ); + aOpts.SetA4Output( psJob->m_forceA4 ); + } + aOpts.SetUseAuxOrigin( aJob->m_useDrillOrigin ); aOpts.SetPlotFrameRef( aJob->m_plotDrawingSheet ); aOpts.SetSubtractMaskFromSilk( aJob->m_subtractSolderMaskFromSilk ); diff --git a/pcbnew/pcbnew_jobs_handler.cpp b/pcbnew/pcbnew_jobs_handler.cpp index 321a39a3ef..2e73df4cfe 100644 --- a/pcbnew/pcbnew_jobs_handler.cpp +++ b/pcbnew/pcbnew_jobs_handler.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -179,6 +180,19 @@ PCBNEW_JOBS_HANDLER::PCBNEW_JOBS_HANDLER( KIWAY* aKiway ) : DIALOG_PLOT dlg( editFrame, aParent, pdfJob ); return dlg.ShowModal() == wxID_OK; } ); + Register( "ps", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPs, this, std::placeholders::_1 ), + [aKiway]( JOB* job, wxWindow* aParent ) -> bool + { + JOB_EXPORT_PCB_PS* psJob = dynamic_cast( job ); + + PCB_EDIT_FRAME* editFrame = + dynamic_cast( aKiway->Player( FRAME_PCB_EDITOR, false ) ); + + wxCHECK( psJob && editFrame, false ); + + DIALOG_PLOT dlg( editFrame, aParent, psJob ); + return dlg.ShowModal() == wxID_OK; + } ); Register( "gerber", std::bind( &PCBNEW_JOBS_HANDLER::JobExportGerber, this, std::placeholders::_1 ), [aKiway]( JOB* job, wxWindow* aParent ) -> bool @@ -1029,6 +1043,90 @@ int PCBNEW_JOBS_HANDLER::JobExportPdf( JOB* aJob ) } +int PCBNEW_JOBS_HANDLER::JobExportPs( JOB* aJob ) +{ + JOB_EXPORT_PCB_PS* psJob = dynamic_cast( aJob ); + + if( psJob == nullptr ) + return CLI::EXIT_CODES::ERR_UNKNOWN; + + BOARD* brd = getBoard( psJob->m_filename ); + + if( !brd ) + return CLI::EXIT_CODES::ERR_INVALID_INPUT_FILE; + + psJob->SetTitleBlock( brd->GetTitleBlock() ); + loadOverrideDrawingSheet( brd, psJob->m_drawingSheet ); + brd->GetProject()->ApplyTextVars( psJob->GetVarOverrides() ); + brd->SynchronizeProperties(); + + if( psJob->m_argLayers ) + psJob->m_plotLayerSequence = convertLayerArg( psJob->m_argLayers.value(), brd ); + + if( psJob->m_argCommonLayers ) + psJob->m_plotOnAllLayersSequence = convertLayerArg( psJob->m_argCommonLayers.value(), brd ); + + if( psJob->m_plotLayerSequence.size() < 1 ) + { + m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR ); + return CLI::EXIT_CODES::ERR_ARGS; + } + + bool isSingle = psJob->m_genMode == JOB_EXPORT_PCB_PS::GEN_MODE::SINGLE; + + if( isSingle ) + { + if( psJob->GetConfiguredOutputPath().IsEmpty() ) + { + wxFileName fn = brd->GetFileName(); + fn.SetName( fn.GetName() ); + fn.SetExt( GetDefaultPlotExtension( PLOT_FORMAT::POST ) ); + + psJob->SetWorkingOutputPath( fn.GetFullName() ); + } + } + + wxString outPath = psJob->GetFullOutputPath( brd->GetProject() ); + + if( !PATHS::EnsurePathExists( outPath, isSingle ) ) + { + m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR ); + return CLI::EXIT_CODES::ERR_INVALID_OUTPUT_CONFLICT; + } + + PCB_PLOT_PARAMS plotOpts; + PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, psJob, *m_reporter ); + + PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts ); + + std::optional layerName; + std::optional sheetName; + std::optional sheetPath; + + if( isSingle ) + { + if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) ) + layerName = psJob->GetVarOverrides().at( wxT( "LAYER" ) ); + + if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) ) + sheetName = psJob->GetVarOverrides().at( wxT( "SHEETNAME" ) ); + + if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) ) + sheetPath = psJob->GetVarOverrides().at( wxT( "SHEETPATH" ) ); + } + + LOCALE_IO dummy; + + if( !pcbPlotter.Plot( outPath, psJob->m_plotLayerSequence, psJob->m_plotOnAllLayersSequence, false, isSingle, + layerName, sheetName, sheetPath ) ) + { + return CLI::EXIT_CODES::ERR_UNKNOWN; + } + + return CLI::EXIT_CODES::OK; +} + + int PCBNEW_JOBS_HANDLER::JobExportGerbers( JOB* aJob ) { int exitCode = CLI::EXIT_CODES::OK; diff --git a/pcbnew/pcbnew_jobs_handler.h b/pcbnew/pcbnew_jobs_handler.h index a54f3f7e6f..33539efd06 100644 --- a/pcbnew/pcbnew_jobs_handler.h +++ b/pcbnew/pcbnew_jobs_handler.h @@ -41,6 +41,7 @@ public: int JobExportSvg( JOB* aJob ); int JobExportDxf( JOB* aJob ); int JobExportPdf( JOB* aJob ); + int JobExportPs( JOB* aJob ); int JobExportGerber( JOB* aJob ); int JobExportGerbers( JOB* aJob ); int JobExportGencad( JOB* aJob );