From ce545f4d6ca0751234497afcbd12085f89642c45 Mon Sep 17 00:00:00 2001 From: Mark Roszko Date: Thu, 19 Jun 2025 19:34:23 -0400 Subject: [PATCH] ADDED: Ability to set a PDF background color for PCB plots Fixes https://gitlab.com/kicad/code/kicad/-/issues/20873 --- common/jobs/job_export_pcb_pdf.cpp | 6 +- common/jobs/job_export_pcb_pdf.h | 3 + kicad/cli/command_pcb_export_base.h | 3 + kicad/cli/command_pcb_export_pdf.cpp | 9 ++ pcbnew/dialogs/dialog_plot.cpp | 28 ++++++ pcbnew/dialogs/dialog_plot.h | 2 + pcbnew/dialogs/dialog_plot_base.cpp | 12 ++- pcbnew/dialogs/dialog_plot_base.fbp | 134 ++++++++++++++++++++++++++- pcbnew/dialogs/dialog_plot_base.h | 4 + pcbnew/pcb_plot_params.cpp | 1 + pcbnew/pcb_plot_params.h | 4 + pcbnew/pcb_plotter.cpp | 1 + pcbnew/plot_board_layers.cpp | 23 +++++ 13 files changed, 227 insertions(+), 3 deletions(-) diff --git a/common/jobs/job_export_pcb_pdf.cpp b/common/jobs/job_export_pcb_pdf.cpp index 86b1e450dd..780093804c 100644 --- a/common/jobs/job_export_pcb_pdf.cpp +++ b/common/jobs/job_export_pcb_pdf.cpp @@ -33,7 +33,10 @@ JOB_EXPORT_PCB_PDF::JOB_EXPORT_PCB_PDF() : JOB_EXPORT_PCB_PLOT( JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::PDF, "pdf", false ), m_pdfFrontFPPropertyPopups( true ), m_pdfBackFPPropertyPopups( true ), - m_pdfMetadata( true ), m_pdfSingle( false ), m_pdfGenMode( GEN_MODE::ALL_LAYERS_ONE_FILE ) + m_pdfMetadata( true ), + m_pdfSingle( false ), + m_pdfGenMode( GEN_MODE::ALL_LAYERS_ONE_FILE ), + m_pdfBackgroundColor( "rgba(0,0,0,0)" ) // represents an unspecified color { m_plotDrawingSheet = false; @@ -51,6 +54,7 @@ JOB_EXPORT_PCB_PDF::JOB_EXPORT_PCB_PDF() : &m_pdfBackFPPropertyPopups, m_pdfBackFPPropertyPopups ) ); m_params.emplace_back( new JOB_PARAM( "pdf_gen_mode", &m_pdfGenMode, m_pdfGenMode ) ); + m_params.emplace_back( new JOB_PARAM( "background_color", &m_pdfBackgroundColor, m_pdfBackgroundColor ) ); } diff --git a/common/jobs/job_export_pcb_pdf.h b/common/jobs/job_export_pcb_pdf.h index 544ad671ba..f9ef115140 100644 --- a/common/jobs/job_export_pcb_pdf.h +++ b/common/jobs/job_export_pcb_pdf.h @@ -56,6 +56,9 @@ public: ///< uused by the cli, will be removed when the other behavior is deprecated GEN_MODE m_pdfGenMode; + + ///< The background color specified in a hex string + wxString m_pdfBackgroundColor; }; #endif diff --git a/kicad/cli/command_pcb_export_base.h b/kicad/cli/command_pcb_export_base.h index 403a1ea9d4..d7814aa2cd 100644 --- a/kicad/cli/command_pcb_export_base.h +++ b/kicad/cli/command_pcb_export_base.h @@ -68,6 +68,9 @@ namespace CLI #define ARG_SCALE "--scale" #define ARG_SCALE_DESC "Scale for the PCB, not for the border and title. Use 0 for autoscale" +#define ARG_BACKGROUND_COLOR "--bg-color" +#define ARG_BACKGROUND_COLOR_DESC "Background color, can be in hex #rrggbb, #rrggbbaa; or css rgb(r,g,b), rgba(r,g,b,a) format" + struct PCB_EXPORT_BASE_COMMAND : public COMMAND { PCB_EXPORT_BASE_COMMAND( const std::string& aName, bool aInputCanBeDir = false, diff --git a/kicad/cli/command_pcb_export_pdf.cpp b/kicad/cli/command_pcb_export_pdf.cpp index 86e3525180..96fc30591c 100644 --- a/kicad/cli/command_pcb_export_pdf.cpp +++ b/kicad/cli/command_pcb_export_pdf.cpp @@ -119,6 +119,11 @@ CLI::PCB_EXPORT_PDF_COMMAND::PCB_EXPORT_PDF_COMMAND() : .scan<'g', double>() .default_value( 1.0 ) .metavar( "SCALE" ); + + m_argParser.add_argument( ARG_BACKGROUND_COLOR ) + .default_value( std::string() ) + .help( UTF8STDSTR( _( ARG_BACKGROUND_COLOR_DESC ) ) ) + .metavar( "COLOR" ); } @@ -157,6 +162,10 @@ int CLI::PCB_EXPORT_PDF_COMMAND::doPerform( KIWAY& aKiway ) pdfJob->m_sketchDNPFPsOnFabLayers = m_argParser.get( ARG_SKETCH_DNP_FPS_ON_FAB_LAYERS ); pdfJob->m_crossoutDNPFPsOnFabLayers = m_argParser.get( ARG_CROSSOUT_DNP_FPS_ON_FAB_LAYERS ); + wxString bgColor = From_UTF8( m_argParser.get( ARG_BACKGROUND_COLOR ).c_str() ); + if( bgColor != wxEmptyString ) + pdfJob->m_pdfBackgroundColor = bgColor; + int drillShape = m_argParser.get( ARG_DRILL_SHAPE_OPTION ); pdfJob->m_drillShapeOption = static_cast( drillShape ); diff --git a/pcbnew/dialogs/dialog_plot.cpp b/pcbnew/dialogs/dialog_plot.cpp index aedd29366e..3efef64958 100644 --- a/pcbnew/dialogs/dialog_plot.cpp +++ b/pcbnew/dialogs/dialog_plot.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,8 @@ DIALOG_PLOT::DIALOG_PLOT( PCB_EDIT_FRAME* aEditFrame, wxWindow* aParent, { BOARD* board = m_editFrame->GetBoard(); + m_pdfBackgroundColorSwatch->SetDefaultColor( COLOR4D::UNSPECIFIED ); + SetName( DLG_WINDOW_NAME ); m_DRCWarningTemplate = m_DRCExclusionsWarning->GetLabel(); @@ -433,6 +436,8 @@ void DIALOG_PLOT::init_Dialog() m_backFPPropertyPopups->SetValue( m_plotOpts.m_PDFBackFPPropertyPopups ); m_pdfMetadata->SetValue( m_plotOpts.m_PDFMetadata ); m_pdfSingle->SetValue( m_plotOpts.m_PDFSingle ); + m_pdfBackgroundColorSwatch->SetSwatchColor( m_plotOpts.m_PDFBackgroundColor, false ); + updatePdfColorOptions(); // Initialize a few other parameters, which can also be modified // from the drill dialog @@ -500,6 +505,7 @@ void DIALOG_PLOT::transferPlotParamsToJob() pdfJob->m_pdfBackFPPropertyPopups = m_plotOpts.m_PDFBackFPPropertyPopups; pdfJob->m_pdfMetadata = m_plotOpts.m_PDFMetadata; pdfJob->m_pdfSingle = m_plotOpts.m_PDFSingle; + pdfJob->m_pdfBackgroundColor = m_plotOpts.m_PDFBackgroundColor.ToCSSString(); // we need to embed this for the cli deprecation fix if( pdfJob->m_pdfSingle ) @@ -1044,6 +1050,7 @@ void DIALOG_PLOT::applyPlotSettings() tempOptions.m_PDFBackFPPropertyPopups = m_backFPPropertyPopups->GetValue(); tempOptions.m_PDFMetadata = m_pdfMetadata->GetValue(); tempOptions.m_PDFSingle = m_pdfSingle->GetValue(); + tempOptions.m_PDFBackgroundColor = m_pdfBackgroundColorSwatch->GetSwatchColor(); } else { @@ -1401,3 +1408,24 @@ void DIALOG_PLOT::onSketchPads( wxCommandEvent& aEvent ) { m_plotPadNumbers->Enable( aEvent.IsChecked() ); } + + +void DIALOG_PLOT::updatePdfColorOptions() +{ + if( m_PDFColorChoice->GetSelection() == 1 ) + { + m_pdfBackgroundColorSwatch->Disable(); + m_pdfBackgroundColorText->Disable(); + } + else + { + m_pdfBackgroundColorSwatch->Enable(); + m_pdfBackgroundColorText->Enable(); + } +} + + +void DIALOG_PLOT::onPDFColorChoice( wxCommandEvent& aEvent ) +{ + updatePdfColorOptions(); +} \ No newline at end of file diff --git a/pcbnew/dialogs/dialog_plot.h b/pcbnew/dialogs/dialog_plot.h index 5ee5aaa5a0..439ac2ddfc 100644 --- a/pcbnew/dialogs/dialog_plot.h +++ b/pcbnew/dialogs/dialog_plot.h @@ -66,6 +66,7 @@ private: void onDNPCheckbox( wxCommandEvent& event ) override; void onSketchPads( wxCommandEvent& event ) override; + void onPDFColorChoice( wxCommandEvent& event ) override; // other functions void init_Dialog(); // main initialization @@ -76,6 +77,7 @@ private: void arrangeAllLayersList( const LSEQ& aSeq ); void loadPlotParamsFromJob(); void transferPlotParamsToJob(); + void updatePdfColorOptions(); private: PCB_EDIT_FRAME* m_editFrame; diff --git a/pcbnew/dialogs/dialog_plot_base.cpp b/pcbnew/dialogs/dialog_plot_base.cpp index 11d679f08a..b65fbeb1d9 100644 --- a/pcbnew/dialogs/dialog_plot_base.cpp +++ b/pcbnew/dialogs/dialog_plot_base.cpp @@ -5,6 +5,7 @@ // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// +#include "widgets/color_swatch.h" #include "widgets/std_bitmap_button.h" #include "widgets/wx_html_report_panel.h" @@ -392,7 +393,14 @@ DIALOG_PLOT_BASE::DIALOG_PLOT_BASE( wxWindow* parent, wxWindowID id, const wxStr gbSizer4->Add( m_pdfMetadata, wxGBPosition( 3, 0 ), wxGBSpan( 1, 2 ), wxLEFT|wxRIGHT, 5 ); m_pdfSingle = new wxCheckBox( m_PDFOptionsSizer->GetStaticBox(), wxID_ANY, _("Single document"), wxDefaultPosition, wxDefaultSize, 0 ); - gbSizer4->Add( m_pdfSingle, wxGBPosition( 4, 0 ), wxGBSpan( 1, 2 ), wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + gbSizer4->Add( m_pdfSingle, wxGBPosition( 4, 0 ), wxGBSpan( 1, 2 ), wxLEFT|wxRIGHT, 5 ); + + m_pdfBackgroundColorText = new wxStaticText( m_PDFOptionsSizer->GetStaticBox(), wxID_ANY, _("Background Color"), wxDefaultPosition, wxDefaultSize, 0 ); + m_pdfBackgroundColorText->Wrap( -1 ); + gbSizer4->Add( m_pdfBackgroundColorText, wxGBPosition( 5, 0 ), wxGBSpan( 1, 1 ), wxALL, 5 ); + + m_pdfBackgroundColorSwatch = new COLOR_SWATCH( m_PDFOptionsSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + gbSizer4->Add( m_pdfBackgroundColorSwatch, wxGBPosition( 5, 1 ), wxGBSpan( 1, 1 ), wxALL, 5 ); m_PDFOptionsSizer->Add( gbSizer4, 1, wxEXPAND|wxBOTTOM, 5 ); @@ -457,6 +465,7 @@ DIALOG_PLOT_BASE::DIALOG_PLOT_BASE( wxWindow* parent, wxWindowID id, const wxStr m_boardSetup->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( DIALOG_PLOT_BASE::onBoardSetup ), NULL, this ); m_useGerberX2Format->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PLOT_BASE::OnGerberX2Checked ), NULL, this ); m_DXF_plotModeOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PLOT_BASE::OnChangeDXFPlotMode ), NULL, this ); + m_PDFColorChoice->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PLOT_BASE::onPDFColorChoice ), NULL, this ); m_buttonDRC->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PLOT_BASE::onRunDRC ), NULL, this ); m_sdbSizer1Apply->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PLOT_BASE::CreateDrillFile ), NULL, this ); m_sdbSizer1OK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PLOT_BASE::Plot ), NULL, this ); @@ -474,6 +483,7 @@ DIALOG_PLOT_BASE::~DIALOG_PLOT_BASE() m_boardSetup->Disconnect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( DIALOG_PLOT_BASE::onBoardSetup ), NULL, this ); m_useGerberX2Format->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PLOT_BASE::OnGerberX2Checked ), NULL, this ); m_DXF_plotModeOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PLOT_BASE::OnChangeDXFPlotMode ), NULL, this ); + m_PDFColorChoice->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PLOT_BASE::onPDFColorChoice ), NULL, this ); m_buttonDRC->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PLOT_BASE::onRunDRC ), NULL, this ); m_sdbSizer1Apply->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PLOT_BASE::CreateDrillFile ), NULL, this ); m_sdbSizer1OK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PLOT_BASE::Plot ), NULL, this ); diff --git a/pcbnew/dialogs/dialog_plot_base.fbp b/pcbnew/dialogs/dialog_plot_base.fbp index e63d36da35..7f813f3b89 100644 --- a/pcbnew/dialogs/dialog_plot_base.fbp +++ b/pcbnew/dialogs/dialog_plot_base.fbp @@ -3812,6 +3812,7 @@ + onPDFColorChoice @@ -4022,7 +4023,7 @@ 5 2 0 - wxBOTTOM|wxLEFT|wxRIGHT + wxLEFT|wxRIGHT 4 1 @@ -4086,6 +4087,137 @@ + + 5 + 1 + 0 + wxALL + 5 + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Background Color + 0 + + 0 + + + 0 + + 1 + m_pdfBackgroundColorText + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + 1 + 1 + wxALL + 5 + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + COLOR_SWATCH + 1 + + + 1 + + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + + 0 + + + 0 + + 1 + m_pdfBackgroundColorSwatch + 1 + + + protected + 1 + + Resizable + + 1 + + COLOR_SWATCH; widgets/color_swatch.h; forward_declare + 0 + + + + + + diff --git a/pcbnew/dialogs/dialog_plot_base.h b/pcbnew/dialogs/dialog_plot_base.h index 859bf842b5..852d10f6f4 100644 --- a/pcbnew/dialogs/dialog_plot_base.h +++ b/pcbnew/dialogs/dialog_plot_base.h @@ -10,6 +10,7 @@ #include #include #include +class COLOR_SWATCH; class STD_BITMAP_BUTTON; class WX_HTML_REPORT_PANEL; @@ -118,6 +119,8 @@ class DIALOG_PLOT_BASE : public DIALOG_SHIM wxCheckBox* m_backFPPropertyPopups; wxCheckBox* m_pdfMetadata; wxCheckBox* m_pdfSingle; + wxStaticText* m_pdfBackgroundColorText; + COLOR_SWATCH* m_pdfBackgroundColorSwatch; WX_HTML_REPORT_PANEL* m_messagesPanel; wxBoxSizer* m_sizerButtons; wxButton* m_buttonDRC; @@ -137,6 +140,7 @@ class DIALOG_PLOT_BASE : public DIALOG_SHIM virtual void onBoardSetup( wxHyperlinkEvent& event ) { event.Skip(); } virtual void OnGerberX2Checked( wxCommandEvent& event ) { event.Skip(); } virtual void OnChangeDXFPlotMode( wxCommandEvent& event ) { event.Skip(); } + virtual void onPDFColorChoice( wxCommandEvent& event ) { event.Skip(); } virtual void onRunDRC( wxCommandEvent& event ) { event.Skip(); } virtual void CreateDrillFile( wxCommandEvent& event ) { event.Skip(); } virtual void Plot( wxCommandEvent& event ) { event.Skip(); } diff --git a/pcbnew/pcb_plot_params.cpp b/pcbnew/pcb_plot_params.cpp index 196abaa491..7d52debab6 100644 --- a/pcbnew/pcb_plot_params.cpp +++ b/pcbnew/pcb_plot_params.cpp @@ -104,6 +104,7 @@ PCB_PLOT_PARAMS::PCB_PLOT_PARAMS() m_PDFBackFPPropertyPopups = true; m_PDFMetadata = true; m_PDFSingle = false; + m_PDFBackgroundColor = COLOR4D::UNSPECIFIED; // This parameter controls if the NPTH pads will be plotted or not // it is a "local" parameter diff --git a/pcbnew/pcb_plot_params.h b/pcbnew/pcb_plot_params.h index 994027fb97..eff83ac666 100644 --- a/pcbnew/pcb_plot_params.h +++ b/pcbnew/pcb_plot_params.h @@ -184,11 +184,15 @@ public: void SetDashedLineGapRatio( double aVal ) { m_dashedLineGapRatio = aVal; } double GetDashedLineGapRatio() const { return m_dashedLineGapRatio; } + void SetPDFBackgroundColor( const COLOR4D& aColor ) { m_PDFBackgroundColor = aColor; } + COLOR4D GetPDFBackgroundColor() const { return m_PDFBackgroundColor; } + public: bool m_PDFFrontFPPropertyPopups; ///< Generate PDF property popup menus for footprints bool m_PDFBackFPPropertyPopups; ///< on front and/or back of board bool m_PDFMetadata; ///< Generate PDF metadata for SUBJECT and AUTHOR bool m_PDFSingle; ///< Generate a single PDF file for all layers + COLOR4D m_PDFBackgroundColor; ///< Background color to use if m_PDFUseBackgroundColor is true private: friend class PCB_PLOT_PARAMS_PARSER; diff --git a/pcbnew/pcb_plotter.cpp b/pcbnew/pcb_plotter.cpp index f0002ff79e..e7ca03f919 100644 --- a/pcbnew/pcb_plotter.cpp +++ b/pcbnew/pcb_plotter.cpp @@ -424,6 +424,7 @@ void PCB_PLOTTER::PlotJobToPlotOpts( PCB_PLOT_PARAMS& aOpts, JOB_EXPORT_PCB_PLOT aOpts.m_PDFBackFPPropertyPopups = pdfJob->m_pdfBackFPPropertyPopups; aOpts.m_PDFMetadata = pdfJob->m_pdfMetadata; aOpts.m_PDFSingle = pdfJob->m_pdfSingle; + aOpts.m_PDFBackgroundColor = COLOR4D( pdfJob->m_pdfBackgroundColor ); } if( aJob->m_plotFormat == JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::POST ) diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp index b856f7ae03..00b57d94dc 100644 --- a/pcbnew/plot_board_layers.cpp +++ b/pcbnew/plot_board_layers.cpp @@ -1187,6 +1187,24 @@ static void FillNegativeKnockout( PLOTTER *aPlotter, const BOX2I &aBbbox ) } +static void plotPdfBackground( BOARD* aBoard, const PCB_PLOT_PARAMS* aPlotOpts, PLOTTER* aPlotter ) +{ + if( aPlotter->GetColorMode() + && aPlotOpts->GetPDFBackgroundColor() != COLOR4D::UNSPECIFIED ) + { + aPlotter->SetColor( aPlotOpts->GetPDFBackgroundColor() ); + + // Use page size selected in pcb to know the schematic bg area + const PAGE_INFO& actualPage = aBoard->GetPageSettings(); + + VECTOR2I end( actualPage.GetWidthIU( pcbIUScale.IU_PER_MILS ), + actualPage.GetHeightIU( pcbIUScale.IU_PER_MILS ) ); + + aPlotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0 ); + } +} + + /** * Open a new plotfile using the options (and especially the format) specified in the options * and prepare the page for plotting. @@ -1309,6 +1327,9 @@ PLOTTER* StartPlotBoard( BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, int aL if( startPlotSuccess ) { + if( aPlotOpts->GetFormat() == PLOT_FORMAT::PDF ) + plotPdfBackground( aBoard, aPlotOpts, plotter ); + // Plot the frame reference if requested if( aPlotOpts->GetPlotFrameRef() ) { @@ -1345,6 +1366,8 @@ void setupPlotterNewPDFPage( PLOTTER* aPlotter, BOARD* aBoard, PCB_PLOT_PARAMS* const wxString& aSheetPath, const wxString& aPageNumber, int aPageCount ) { + plotPdfBackground( aBoard, aPlotOpts, aPlotter ); + // Plot the frame reference if requested if( aPlotOpts->GetPlotFrameRef() ) {