7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-21 14:31:42 +00:00

ADDED: "Single document" PDF plot option for PCB plots

This commit is contained in:
Marek Roszko 2024-12-30 23:51:42 -05:00
parent d033b7bebb
commit 17a1706257
9 changed files with 252 additions and 49 deletions

View File

@ -50,6 +50,7 @@
#include <jobs/job_export_pcb_dxf.h>
#include <jobs/job_export_pcb_pdf.h>
#include <jobs/job_export_pcb_svg.h>
#include <plotters/plotters_pslike.h>
#include <wx/dirdlg.h>
#include <wx/msgdlg.h>
@ -411,6 +412,7 @@ void DIALOG_PLOT::init_Dialog()
m_frontFPPropertyPopups->SetValue( m_plotOpts.m_PDFFrontFPPropertyPopups );
m_backFPPropertyPopups->SetValue( m_plotOpts.m_PDFBackFPPropertyPopups );
m_pdfMetadata->SetValue( m_plotOpts.m_PDFMetadata );
m_pdfSingle->SetValue( m_plotOpts.m_PDFSingle );
// Initialize a few other parameters, which can also be modified
// from the drill dialog
@ -1114,6 +1116,7 @@ void DIALOG_PLOT::applyPlotSettings()
tempOptions.m_PDFFrontFPPropertyPopups = m_frontFPPropertyPopups->GetValue();
tempOptions.m_PDFBackFPPropertyPopups = m_backFPPropertyPopups->GetValue();
tempOptions.m_PDFMetadata = m_pdfMetadata->GetValue();
tempOptions.m_PDFSingle = m_pdfSingle->GetValue();
}
else
{
@ -1369,38 +1372,17 @@ void DIALOG_PLOT::Plot( wxCommandEvent& event )
wxBusyCursor dummy;
for( PCB_LAYER_ID layer : m_plotOpts.GetLayerSelection().UIOrder() )
if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF &&
m_plotOpts.m_PDFSingle )
{
LSEQ plotSequence;
// Base layer always gets plotted first.
plotSequence.push_back( layer );
}
// Add selected layers from plot on all layers list in order set by user.
wxArrayInt plotOnAllLayers;
if( m_plotAllLayersList->GetCheckedItems( plotOnAllLayers ) )
{
size_t count = plotOnAllLayers.GetCount();
for( size_t i = 0; i < count; i++ )
{
int index = plotOnAllLayers.Item( i );
PCB_LAYER_ID client_layer = getLayerClientData( m_plotAllLayersList, index )->Layer();
// Don't plot the same layer more than once;
if( find( plotSequence.begin(), plotSequence.end(), client_layer ) != plotSequence.end() )
continue;
plotSequence.push_back( client_layer );
}
}
wxString layerName = board->GetLayerName( layer );
//@todo allow controlling the sheet name and path that will be displayed in the title block
// Leave blank for now
wxString sheetName;
wxString sheetPath;
LSEQ layersToPlot = m_plotOpts.GetLayerSelection().UIOrder();
size_t finalPageCount = 0;
for( size_t i = 0; i < layersToPlot.size(); i++ )
{
PCB_LAYER_ID layer = layersToPlot[i];
// All copper layers that are disabled are actually selected
// This is due to wonkyness in automatically selecting copper layers
@ -1411,6 +1393,30 @@ void DIALOG_PLOT::Plot( wxCommandEvent& event )
if( ( LSET::AllCuMask() & ~board->GetEnabledLayers() )[layer] )
continue;
finalPageCount++;
}
PLOTTER* plotter = nullptr;
for( size_t i = 0, pageNum = 1; i < layersToPlot.size(); i++ )
{
PCB_LAYER_ID layer = layersToPlot[i];
// All copper layers that are disabled are actually selected
// This is due to wonkyness in automatically selecting copper layers
// for plotting when adding more than two layers to a board.
// If plot options become accessible to the layers setup dialog
// please move this functionality there!
// This skips a copper layer if it is actually disabled on the board.
if( ( LSET::AllCuMask() & ~board->GetEnabledLayers() )[layer] )
continue;
LSEQ plotSequence = getPlotSequence( layer );
wxString layerName = board->GetLayerName( layer );
//@todo allow controlling the sheet name and path that will be displayed in the title block
// Leave blank for now
wxString sheetPath;
// Pick the basename from the board file
wxFileName fn( boardFilename );
@ -1419,14 +1425,37 @@ void DIALOG_PLOT::Plot( wxCommandEvent& event )
if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && m_useGerberExtensions->GetValue() )
file_ext = GetGerberProtelExtension( layer );
BuildPlotFileName( &fn, outputDir.GetPath(), layerName, file_ext );
wxString fullname = fn.GetFullName();
jobfile_writer.AddGbrFile( layer, fullname );
if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF && m_plotOpts.m_PDFSingle )
{
fn.SetExt( FILEEXT::PdfFileExtension );
}
else
{
BuildPlotFileName( &fn, outputDir.GetPath(), layerName, file_ext );
}
if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER )
{
wxString fullname = fn.GetFullName();
jobfile_writer.AddGbrFile( layer, fullname );
}
LOCALE_IO toggle;
PLOTTER* plotter = StartPlotBoard( board, &m_plotOpts, layer, layerName, fn.GetFullPath(),
sheetName, sheetPath );
if( m_plotOpts.GetFormat() != PLOT_FORMAT::PDF
|| !m_plotOpts.m_PDFSingle
|| ( i == 0 && m_plotOpts.GetFormat() == PLOT_FORMAT::PDF
&& m_plotOpts.m_PDFSingle ) )
{
// this will only be used by pdf
wxString pageNumber = wxString::Format( "%zu", pageNum );
wxString pageName = layerName;
wxString sheetName = layerName;
plotter = StartPlotBoard( board, &m_plotOpts, layer, layerName, fn.GetFullPath(),
sheetName, sheetPath, pageName, pageNumber,
finalPageCount );
}
// Print diags in messages box:
wxString msg;
@ -1450,12 +1479,36 @@ void DIALOG_PLOT::Plot( wxCommandEvent& event )
PlotBoardLayers( board, plotter, plotSequence, m_plotOpts );
PlotInteractiveLayer( board, plotter, m_plotOpts );
plotter->EndPlot();
delete plotter->RenderSettings();
delete plotter;
msg.Printf( _( "Plotted to '%s'." ), fn.GetFullPath() );
reporter.Report( msg, RPT_SEVERITY_ACTION );
if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF && m_plotOpts.m_PDFSingle &&
i != layersToPlot.size() - 1)
{
// this will only be used by pdf
// this will only be used by pdf
wxString pageNumber = wxString::Format( "%zu", pageNum + 1 );
PCB_LAYER_ID nextLayer = layersToPlot[i + 1];
wxString pageName = board->GetLayerName( nextLayer );
wxString sheetName = layerName;
static_cast<PDF_PLOTTER*>( plotter )->ClosePage();
static_cast<PDF_PLOTTER*>( plotter )->StartPage( pageNumber, pageName );
setupPlotterNewPDFPage( plotter, board, &m_plotOpts, sheetName, sheetPath,
pageNumber, finalPageCount );
}
if( m_plotOpts.GetFormat() != PLOT_FORMAT::PDF
|| !m_plotOpts.m_PDFSingle
|| i == layersToPlot.size()-1 )
{
plotter->EndPlot();
delete plotter->RenderSettings();
delete plotter;
plotter = nullptr;
msg.Printf( _( "Plotted to '%s'." ), fn.GetFullPath() );
reporter.Report( msg, RPT_SEVERITY_ACTION );
}
}
else
{
@ -1463,6 +1516,8 @@ void DIALOG_PLOT::Plot( wxCommandEvent& event )
reporter.Report( msg, RPT_SEVERITY_ERROR );
}
pageNum++;
wxSafeYield(); // displays report message.
}
@ -1481,6 +1536,38 @@ void DIALOG_PLOT::Plot( wxCommandEvent& event )
}
}
LSEQ DIALOG_PLOT::getPlotSequence( PCB_LAYER_ID aLayerToPlot )
{
LSEQ plotSequence;
// Base layer always gets plotted first.
plotSequence.push_back( aLayerToPlot );
// Add selected layers from plot on all layers list in order set by user.
wxArrayInt plotOnAllLayers;
if( m_plotAllLayersList->GetCheckedItems( plotOnAllLayers ) )
{
size_t count = plotOnAllLayers.GetCount();
for( size_t i = 0; i < count; i++ )
{
int index = plotOnAllLayers.Item( i );
PCB_LAYER_ID client_layer = getLayerClientData( m_plotAllLayersList, index )->Layer();
// Don't plot the same layer more than once;
if( find( plotSequence.begin(), plotSequence.end(), client_layer )
!= plotSequence.end() )
continue;
plotSequence.push_back( client_layer );
}
}
return plotSequence;
}
void DIALOG_PLOT::onRunDRC( wxCommandEvent& event )
{

View File

@ -83,6 +83,8 @@ private:
void loadPlotParamsFromJob();
void transferPlotParamsToJob();
LSEQ getPlotSequence( PCB_LAYER_ID aLayerToPlot );
private:
PCB_EDIT_FRAME* m_editFrame;
LSEQ m_layerList; // List to hold CheckListBox layer numbers

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -420,7 +420,10 @@ DIALOG_PLOT_BASE::DIALOG_PLOT_BASE( wxWindow* parent, wxWindowID id, const wxStr
m_pdfMetadata = new wxCheckBox( m_PDFOptionsSizer->GetStaticBox(), wxID_ANY, _("Generate metadata from AUTHOR && SUBJECT variables"), wxDefaultPosition, wxDefaultSize, 0 );
m_pdfMetadata->SetToolTip( _("Generate PDF document properties from AUTHOR and SUBJECT text variables") );
gbSizer4->Add( m_pdfMetadata, wxGBPosition( 3, 0 ), wxGBSpan( 1, 2 ), wxBOTTOM|wxRIGHT|wxLEFT, 5 );
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 );
m_PDFOptionsSizer->Add( gbSizer4, 1, wxEXPAND|wxBOTTOM, 5 );

View File

@ -4301,7 +4301,7 @@
<property name="border">5</property>
<property name="colspan">2</property>
<property name="column">0</property>
<property name="flag">wxBOTTOM|wxRIGHT|wxLEFT</property>
<property name="flag">wxLEFT|wxRIGHT</property>
<property name="row">3</property>
<property name="rowspan">1</property>
<object class="wxCheckBox" expanded="true">
@ -4365,6 +4365,74 @@
<property name="window_style"></property>
</object>
</object>
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="colspan">2</property>
<property name="column">0</property>
<property name="flag">wxBOTTOM|wxLEFT|wxRIGHT</property>
<property name="row">4</property>
<property name="rowspan">1</property>
<object class="wxCheckBox" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Single document</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_pdfSingle</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
</object>
</object>
</object>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -123,6 +123,7 @@ class DIALOG_PLOT_BASE : public DIALOG_SHIM
wxCheckBox* m_frontFPPropertyPopups;
wxCheckBox* m_backFPPropertyPopups;
wxCheckBox* m_pdfMetadata;
wxCheckBox* m_pdfSingle;
WX_HTML_REPORT_PANEL* m_messagesPanel;
wxBoxSizer* m_sizerButtons;
wxButton* m_buttonDRC;

View File

@ -140,6 +140,7 @@ PCB_PLOT_PARAMS::PCB_PLOT_PARAMS()
m_PDFFrontFPPropertyPopups = true;
m_PDFBackFPPropertyPopups = true;
m_PDFMetadata = true;
m_PDFSingle = false;
// This parameter controls if the NPTH pads will be plotted or not
// it is a "local" parameter

View File

@ -202,6 +202,7 @@ 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
private:
friend class PCB_PLOT_PARAMS_PARSER;

View File

@ -157,9 +157,15 @@ private:
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& aSheetName, const wxString& aSheetPath,
const wxString& aPageName = wxT( "1" ),
const wxString& aPageNumber = wxEmptyString,
const int aPageCount = 1);
/**
void setupPlotterNewPDFPage( PLOTTER* aPlotter, BOARD* aBoard, const PCB_PLOT_PARAMS* aPlotOpts,
const wxString& aSheetName, const wxString& aSheetPath,
const wxString& aPageNumber, int aPageCount );
/**
* Plot a sequence of board layer IDs.
*
* @param aBoard is the board to plot.

View File

@ -1205,7 +1205,9 @@ static void ConfigureHPGLPenSizes( HPGL_PLOTTER *aPlotter, const PCB_PLOT_PARAMS
*/
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& aSheetName, const wxString& aSheetPath,
const wxString& aPageName, const wxString& aPageNumber,
const int aPageCount )
{
wxCHECK( aBoard && aPlotOpts, nullptr );
@ -1300,14 +1302,26 @@ PLOTTER* StartPlotBoard( BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, int aL
AddGerberX2Attribute( plotter, aBoard, aLayer, not useX2mode );
}
if( plotter->StartPlot( wxT( "1" ) ) )
bool startPlotSuccess = false;
if (plotter->GetPlotterType() == PLOT_FORMAT::PDF)
{
startPlotSuccess =
static_cast<PDF_PLOTTER*>( plotter )->StartPlot( aPageNumber, aPageName );
}
else
{
startPlotSuccess = plotter->StartPlot( aPageName );
}
if( startPlotSuccess )
{
// Plot the frame reference if requested
if( aPlotOpts->GetPlotFrameRef() )
{
PlotDrawingSheet( plotter, aBoard->GetProject(), aBoard->GetTitleBlock(),
aBoard->GetPageSettings(), &aBoard->GetProperties(), wxT( "1" ),
1, aSheetName, aSheetPath, aBoard->GetFileName(),
aBoard->GetPageSettings(), &aBoard->GetProperties(), aPageNumber,
aPageCount, aSheetName, aSheetPath, aBoard->GetFileName(),
renderSettings->GetLayerColor( LAYER_DRAWINGSHEET ) );
if( aPlotOpts->GetMirror() )
@ -1331,3 +1345,23 @@ PLOTTER* StartPlotBoard( BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, int aL
delete plotter;
return nullptr;
}
void setupPlotterNewPDFPage( PLOTTER* aPlotter,
BOARD* aBoard,
const PCB_PLOT_PARAMS* aPlotOpts,
const wxString& aSheetName, const wxString& aSheetPath,
const wxString& aPageNumber, int aPageCount )
{
// Plot the frame reference if requested
if( aPlotOpts->GetPlotFrameRef() )
{
PlotDrawingSheet( aPlotter, aBoard->GetProject(), aBoard->GetTitleBlock(),
aBoard->GetPageSettings(), &aBoard->GetProperties(), aPageNumber,
aPageCount,
aSheetName, aSheetPath, aBoard->GetFileName(),
aPlotter->RenderSettings()->GetLayerColor( LAYER_DRAWINGSHEET ) );
if( aPlotOpts->GetMirror() )
initializePlotter( aPlotter, aBoard, aPlotOpts );
}
}