mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-21 16:43:44 +00:00
Lets move the chunk of plot logic from dialog_plot and share it with the jobs handler
More factoring in V10. This is just because it's getting annoying really lol
This commit is contained in:
parent
d8b8d8aa3c
commit
cad0e3fc27
@ -359,6 +359,7 @@ set( PCBNEW_CLASS_SRCS
|
||||
pcb_base_edit_frame.cpp
|
||||
pcb_layer_box_selector.cpp
|
||||
pcb_edit_frame.cpp
|
||||
pcb_plotter.cpp
|
||||
pcbnew_config.cpp
|
||||
pcbnew_jobs_handler.cpp
|
||||
pcbnew_printout.cpp
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include <jobs/job_export_pcb_pdf.h>
|
||||
#include <jobs/job_export_pcb_svg.h>
|
||||
#include <plotters/plotters_pslike.h>
|
||||
#include <pcb_plotter.h>
|
||||
|
||||
#include <wx/dirdlg.h>
|
||||
#include <wx/msgdlg.h>
|
||||
@ -1384,8 +1385,6 @@ void DIALOG_PLOT::Plot( wxCommandEvent& event )
|
||||
m_plotOpts.SetWidthAdjust( m_PSWidthAdjust );
|
||||
}
|
||||
|
||||
wxString file_ext( GetDefaultPlotExtension( m_plotOpts.GetFormat() ) );
|
||||
|
||||
// Test for a reasonable scale value
|
||||
// XXX could this actually happen? isn't it constrained in the apply function?
|
||||
if( m_plotOpts.GetScale() < PLOT_MIN_SCALE )
|
||||
@ -1394,208 +1393,36 @@ void DIALOG_PLOT::Plot( wxCommandEvent& event )
|
||||
if( m_plotOpts.GetScale() > PLOT_MAX_SCALE )
|
||||
DisplayInfoMessage( this, _( "Warning: Scale option set to a very large value" ) );
|
||||
|
||||
GERBER_JOBFILE_WRITER jobfile_writer( board, &reporter );
|
||||
|
||||
// Save the current plot options in the board
|
||||
m_editFrame->SetPlotSettings( m_plotOpts );
|
||||
|
||||
wxBusyCursor dummy;
|
||||
PCB_PLOTTER pcbPlotter( m_editFrame->GetBoard(), &reporter, m_plotOpts );
|
||||
|
||||
if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF &&
|
||||
m_plotOpts.m_PDFSingle )
|
||||
LSEQ layersToPlot = m_plotOpts.GetLayerSelection().UIOrder();
|
||||
|
||||
wxArrayInt plotOnAllLayers;
|
||||
LSEQ commonLayers;
|
||||
|
||||
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();
|
||||
|
||||
commonLayers.push_back( client_layer );
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// 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;
|
||||
|
||||
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 );
|
||||
|
||||
// Use Gerber Extensions based on layer number
|
||||
// (See http://en.wikipedia.org/wiki/Gerber_File)
|
||||
if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && m_useGerberExtensions->GetValue() )
|
||||
file_ext = GetGerberProtelExtension( layer );
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if( plotter )
|
||||
{
|
||||
plotter->SetTitle( ExpandTextVars( board->GetTitleBlock().GetTitle(), &textResolver ) );
|
||||
|
||||
if( m_plotOpts.m_PDFMetadata )
|
||||
{
|
||||
msg = wxS( "AUTHOR" );
|
||||
|
||||
if( board->ResolveTextVar( &msg, 0 ) )
|
||||
plotter->SetAuthor( msg );
|
||||
|
||||
msg = wxS( "SUBJECT" );
|
||||
|
||||
if( board->ResolveTextVar( &msg, 0 ) )
|
||||
plotter->SetSubject( msg );
|
||||
}
|
||||
|
||||
PlotBoardLayers( board, plotter, plotSequence, m_plotOpts );
|
||||
PlotInteractiveLayer( board, plotter, m_plotOpts );
|
||||
|
||||
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
|
||||
{
|
||||
msg.Printf( _( "Failed to create file '%s'." ), fn.GetFullPath() );
|
||||
reporter.Report( msg, RPT_SEVERITY_ERROR );
|
||||
}
|
||||
|
||||
pageNum++;
|
||||
|
||||
wxSafeYield(); // displays report message.
|
||||
}
|
||||
|
||||
if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && m_plotOpts.GetCreateGerberJobFile() )
|
||||
{
|
||||
// Pick the basename from the board file
|
||||
wxFileName fn( boardFilename );
|
||||
|
||||
// Build gerber job file from basename
|
||||
BuildPlotFileName( &fn, outputDir.GetPath(), wxT( "job" ),
|
||||
FILEEXT::GerberJobFileExtension );
|
||||
jobfile_writer.CreateJobFile( fn.GetFullPath() );
|
||||
}
|
||||
|
||||
reporter.ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
|
||||
pcbPlotter.Plot( outputDir.GetPath(), layersToPlot, commonLayers,
|
||||
m_useGerberExtensions->GetValue() );
|
||||
}
|
||||
}
|
||||
|
||||
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 )
|
||||
|
@ -83,8 +83,6 @@ 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
|
||||
|
259
pcbnew/pcb_plotter.cpp
Normal file
259
pcbnew/pcb_plotter.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 1992-2024 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 2
|
||||
* 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, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <pcb_plotter.h>
|
||||
#include <plotters/plotter.h>
|
||||
#include <plotters/plotters_pslike.h>
|
||||
#include <board.h>
|
||||
#include <reporter.h>
|
||||
#include <pcbplot.h>
|
||||
#include <wx/filename.h>
|
||||
#include <gerber_jobfile_writer.h>
|
||||
|
||||
|
||||
PCB_PLOTTER::PCB_PLOTTER( BOARD* aBoard, REPORTER* aReporter, PCB_PLOT_PARAMS& aParams ) :
|
||||
m_board( aBoard ), m_plotOpts( aParams ), m_reporter( aReporter )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool PCB_PLOTTER::Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot,
|
||||
const LSEQ& aCommonLayers, bool aUseGerberFileExtensions )
|
||||
{
|
||||
std::function<bool( wxString* )> textResolver = [&]( wxString* token ) -> bool
|
||||
{
|
||||
// Handles board->GetTitleBlock() *and* board->GetProject()
|
||||
return m_board->ResolveTextVar( token, 0 );
|
||||
};
|
||||
|
||||
size_t finalPageCount = 0;
|
||||
for( size_t i = 0; i < aLayersToPlot.size(); i++ )
|
||||
{
|
||||
PCB_LAYER_ID layer = aLayersToPlot[i];
|
||||
if( copperLayerShouldBeSkipped( layer ) )
|
||||
continue;
|
||||
|
||||
finalPageCount++;
|
||||
}
|
||||
|
||||
std::unique_ptr<GERBER_JOBFILE_WRITER> jobfile_writer;
|
||||
|
||||
if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER )
|
||||
{
|
||||
jobfile_writer = std::make_unique<GERBER_JOBFILE_WRITER>( m_board, m_reporter );
|
||||
}
|
||||
|
||||
wxString fileExt( GetDefaultPlotExtension( m_plotOpts.GetFormat() ) );
|
||||
wxString sheetPath;
|
||||
PLOTTER* plotter = nullptr;
|
||||
for( size_t i = 0, pageNum = 1; i < aLayersToPlot.size(); i++ )
|
||||
{
|
||||
PCB_LAYER_ID layer = aLayersToPlot[i];
|
||||
|
||||
if( copperLayerShouldBeSkipped( layer ) )
|
||||
continue;
|
||||
|
||||
LSEQ plotSequence = getPlotSequence( layer, aCommonLayers );
|
||||
|
||||
wxString layerName = m_board->GetLayerName( layer );
|
||||
|
||||
wxFileName fn( aOutputPath );
|
||||
wxFileName brdFn = m_board->GetFileName();
|
||||
wxString msg;
|
||||
fn.SetName( 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 )
|
||||
{
|
||||
wxString fullname = fn.GetFullName();
|
||||
jobfile_writer->AddGbrFile( layer, fullname );
|
||||
}
|
||||
|
||||
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( m_board, &m_plotOpts, layer, layerName, fn.GetFullPath(),
|
||||
sheetName,
|
||||
sheetPath, pageName, pageNumber, finalPageCount );
|
||||
}
|
||||
|
||||
if( plotter )
|
||||
{
|
||||
plotter->SetTitle( ExpandTextVars( m_board->GetTitleBlock().GetTitle(), &textResolver ) );
|
||||
|
||||
if( m_plotOpts.m_PDFMetadata )
|
||||
{
|
||||
msg = wxS( "AUTHOR" );
|
||||
|
||||
if( m_board->ResolveTextVar( &msg, 0 ) )
|
||||
plotter->SetAuthor( msg );
|
||||
|
||||
msg = wxS( "SUBJECT" );
|
||||
|
||||
if( m_board->ResolveTextVar( &msg, 0 ) )
|
||||
plotter->SetSubject( msg );
|
||||
}
|
||||
|
||||
PlotBoardLayers( m_board, plotter, plotSequence, m_plotOpts );
|
||||
PlotInteractiveLayer( m_board, plotter, m_plotOpts );
|
||||
|
||||
|
||||
if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF && m_plotOpts.m_PDFSingle
|
||||
&& i != aLayersToPlot.size() - 1 )
|
||||
{
|
||||
wxString pageNumber = wxString::Format( "%zu", pageNum + 1 );
|
||||
PCB_LAYER_ID nextLayer = aLayersToPlot[i + 1];
|
||||
wxString pageName = m_board->GetLayerName( nextLayer );
|
||||
wxString sheetName = layerName;
|
||||
|
||||
static_cast<PDF_PLOTTER*>( plotter )->ClosePage();
|
||||
static_cast<PDF_PLOTTER*>( plotter )->StartPage( pageNumber, pageName );
|
||||
setupPlotterNewPDFPage( plotter, m_board, &m_plotOpts, sheetName, sheetPath,
|
||||
pageNumber,
|
||||
finalPageCount );
|
||||
}
|
||||
|
||||
|
||||
// last page
|
||||
if( m_plotOpts.GetFormat() != PLOT_FORMAT::PDF || !m_plotOpts.m_PDFSingle
|
||||
|| i == aLayersToPlot.size() - 1 )
|
||||
{
|
||||
plotter->EndPlot();
|
||||
delete plotter->RenderSettings();
|
||||
delete plotter;
|
||||
plotter = nullptr;
|
||||
|
||||
msg.Printf( _( "Plotted to '%s'." ), fn.GetFullPath() );
|
||||
m_reporter->Report( msg, RPT_SEVERITY_ACTION );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Printf( _( "Failed to create file '%s'." ), fn.GetFullPath() );
|
||||
m_reporter->Report( msg, RPT_SEVERITY_ERROR );
|
||||
|
||||
if( m_plotOpts.m_PDFSingle )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pageNum++;
|
||||
|
||||
wxSafeYield(); // displays report message.
|
||||
}
|
||||
|
||||
if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && m_plotOpts.GetCreateGerberJobFile() )
|
||||
{
|
||||
// Pick the basename from the board file
|
||||
wxFileName fn( m_board->GetFileName() );
|
||||
|
||||
// Build gerber job file from basename
|
||||
BuildPlotFileName( &fn, aOutputPath, wxT( "job" ),
|
||||
FILEEXT::GerberJobFileExtension );
|
||||
jobfile_writer->CreateJobFile( fn.GetFullPath() );
|
||||
}
|
||||
|
||||
m_reporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PCB_PLOTTER::copperLayerShouldBeSkipped( PCB_LAYER_ID aLayerToPlot )
|
||||
{
|
||||
return ( LSET::AllCuMask() & ~m_board->GetEnabledLayers() )[aLayerToPlot];
|
||||
}
|
||||
|
||||
|
||||
void PCB_PLOTTER::BuildPlotFileName( wxFileName* aFilename, const wxString& aOutputDir,
|
||||
const wxString& aSuffix, const wxString& aExtension )
|
||||
{
|
||||
// aFilename contains the base filename only (without path and extension)
|
||||
// when calling this function.
|
||||
// It is expected to be a valid filename (this is usually the board filename)
|
||||
aFilename->SetPath( aOutputDir );
|
||||
|
||||
// Set the file extension
|
||||
aFilename->SetExt( aExtension );
|
||||
|
||||
// remove leading and trailing spaces if any from the suffix, if
|
||||
// something survives add it to the name;
|
||||
// also the suffix can contain some not allowed chars in filename (/ \ . : and some others),
|
||||
// so change them to underscore
|
||||
// Remember it can be called from a python script, so the illegal chars
|
||||
// have to be filtered here.
|
||||
wxString suffix = aSuffix;
|
||||
suffix.Trim( true );
|
||||
suffix.Trim( false );
|
||||
|
||||
wxString badchars = wxFileName::GetForbiddenChars( wxPATH_DOS );
|
||||
badchars.Append( "%." );
|
||||
|
||||
for( unsigned ii = 0; ii < badchars.Len(); ii++ )
|
||||
suffix.Replace( badchars[ii], wxT( "_" ) );
|
||||
|
||||
if( !suffix.IsEmpty() )
|
||||
aFilename->SetName( aFilename->GetName() + wxT( "-" ) + suffix );
|
||||
}
|
||||
|
||||
|
||||
LSEQ PCB_PLOTTER::getPlotSequence( PCB_LAYER_ID aLayerToPlot, LSEQ aPlotWithAllLayersSeq )
|
||||
{
|
||||
LSEQ plotSequence;
|
||||
|
||||
// Base layer always gets plotted first.
|
||||
plotSequence.push_back( aLayerToPlot );
|
||||
|
||||
for( size_t i = 0; i < aPlotWithAllLayersSeq.size(); i++ )
|
||||
{
|
||||
PCB_LAYER_ID layer = aPlotWithAllLayersSeq[i];
|
||||
|
||||
// Don't plot the same layer more than once;
|
||||
if( find( plotSequence.begin(), plotSequence.end(), layer ) != plotSequence.end() )
|
||||
continue;
|
||||
|
||||
plotSequence.push_back( layer );
|
||||
}
|
||||
|
||||
return plotSequence;
|
||||
}
|
76
pcbnew/pcb_plotter.h
Normal file
76
pcbnew/pcb_plotter.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 1992-2024 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 2
|
||||
* 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, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pcb_plot_params.h>
|
||||
|
||||
class BOARD;
|
||||
class REPORTER;
|
||||
class wxFileName;
|
||||
|
||||
class PCB_PLOTTER
|
||||
{
|
||||
public:
|
||||
PCB_PLOTTER( BOARD* aBoard, REPORTER* aReporter, PCB_PLOT_PARAMS& aParams );
|
||||
|
||||
bool Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot, const LSEQ& aCommonLayers,
|
||||
bool aUseGerberFileExtensions );
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
bool copperLayerShouldBeSkipped( PCB_LAYER_ID aLayerToPlot );
|
||||
|
||||
/**
|
||||
* Complete a plot filename.
|
||||
*
|
||||
* It forces the output directory, adds a suffix to the name, and sets the specified extension.
|
||||
* The suffix is usually the layer name and replaces illegal file name character in the suffix
|
||||
* with an underscore character.
|
||||
*
|
||||
* @param aFilename is the file name to initialize that contains the base filename.
|
||||
* @param aOutputDir is the path.
|
||||
* @param aSuffix is the suffix to add to the base filename.
|
||||
* @param aExtension is the file extension.
|
||||
*/
|
||||
static void BuildPlotFileName( wxFileName* aFilename, const wxString& aOutputDir, const wxString& aSuffix,
|
||||
const wxString& aExtension );
|
||||
|
||||
protected:
|
||||
BOARD* m_board;
|
||||
PCB_PLOT_PARAMS m_plotOpts;
|
||||
REPORTER* m_reporter;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Generates a final LSEQ for plotting by removing duplicates
|
||||
*/
|
||||
LSEQ getPlotSequence( PCB_LAYER_ID aLayerToPlot, LSEQ aPlotWithAllLayersSeq );
|
||||
|
||||
};
|
@ -67,6 +67,7 @@
|
||||
#include <netlist_reader/netlist_reader.h>
|
||||
#include <pcbnew_settings.h>
|
||||
#include <pcbplot.h>
|
||||
#include <pcb_plotter.h>
|
||||
#include <pgm_base.h>
|
||||
#include <3d_rendering/raytracing/render_3d_raytrace_ram.h>
|
||||
#include <3d_rendering/track_ball.h>
|
||||
@ -879,6 +880,13 @@ int PCBNEW_JOBS_HANDLER::JobExportPdf( JOB* aJob )
|
||||
}
|
||||
else
|
||||
{
|
||||
// ensure this is set for this one gen mode
|
||||
if( aPdfJob->m_pdfGenMode == JOB_EXPORT_PCB_PDF::GEN_MODE::ONE_PAGE_PER_LAYER_ONE_FILE )
|
||||
{
|
||||
plotOpts.m_PDFSingle = true;
|
||||
}
|
||||
|
||||
PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts );
|
||||
// ensure output dir exists
|
||||
wxFileName fnOut( aPdfJob->GetFullOutputPath() + wxT( "/" ) );
|
||||
|
||||
@ -888,130 +896,10 @@ int PCBNEW_JOBS_HANDLER::JobExportPdf( JOB* aJob )
|
||||
return CLI::EXIT_CODES::ERR_INVALID_OUTPUT_CONFLICT;
|
||||
}
|
||||
|
||||
std::function<bool( wxString* )> textResolver = [&]( wxString* token ) -> bool
|
||||
if( !pcbPlotter.Plot( aPdfJob->GetFullOutputPath(), aPdfJob->m_printMaskLayer,
|
||||
aPdfJob->m_printMaskLayersToIncludeOnAllLayers, false ) )
|
||||
{
|
||||
// Handles board->GetTitleBlock() *and* board->GetProject()
|
||||
return brd->ResolveTextVar( token, 0 );
|
||||
};
|
||||
|
||||
size_t finalPageCount = 0;
|
||||
for( size_t i = 0; i < aPdfJob->m_printMaskLayer.size(); i++ )
|
||||
{
|
||||
PCB_LAYER_ID layer = aPdfJob->m_printMaskLayer[i];
|
||||
if( ( LSET::AllCuMask() & ~brd->GetEnabledLayers() )[layer] )
|
||||
continue;
|
||||
|
||||
finalPageCount++;
|
||||
}
|
||||
|
||||
wxString sheetPath;
|
||||
PLOTTER* plotter = nullptr;
|
||||
for( size_t i = 0, pageNum = 1; i < aPdfJob->m_printMaskLayer.size(); i++ )
|
||||
{
|
||||
PCB_LAYER_ID layer = aPdfJob->m_printMaskLayer[i];
|
||||
|
||||
if( ( LSET::AllCuMask() & ~brd->GetEnabledLayers() )[layer] )
|
||||
continue;
|
||||
|
||||
LSEQ plotSequence = getPlotSequence( layer, aPdfJob->m_printMaskLayersToIncludeOnAllLayers );
|
||||
|
||||
wxString layerName = brd->GetLayerName( layer );
|
||||
|
||||
wxFileName fn( aPdfJob->GetFullOutputPath() );
|
||||
wxFileName brdFn = brd->GetFileName();
|
||||
wxString msg;
|
||||
fn.SetName( brdFn.GetName() );
|
||||
|
||||
if( aPdfJob->m_pdfGenMode == JOB_EXPORT_PCB_PDF::GEN_MODE::ONE_PAGE_PER_LAYER_ONE_FILE )
|
||||
{
|
||||
fn.SetExt( GetDefaultPlotExtension( PLOT_FORMAT::PDF ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
fn.SetExt( GetDefaultPlotExtension( PLOT_FORMAT::PDF ) );
|
||||
fn.SetName( fn.GetName() + wxString::Format( wxT( "_%s" ), layerName ) );
|
||||
}
|
||||
|
||||
if( ( aPdfJob->m_pdfGenMode == JOB_EXPORT_PCB_PDF::GEN_MODE::ONE_PAGE_PER_LAYER_ONE_FILE
|
||||
&& i == 0 )
|
||||
|| aPdfJob->m_pdfGenMode
|
||||
== JOB_EXPORT_PCB_PDF::GEN_MODE::ALL_LAYERS_SEPARATE_FILE )
|
||||
{
|
||||
// this will only be used by pdf
|
||||
wxString pageNumber = wxString::Format( "%zu", pageNum );
|
||||
wxString pageName = layerName;
|
||||
wxString sheetName = layerName;
|
||||
|
||||
plotter = StartPlotBoard( brd, &plotOpts, layer, layerName, fn.GetFullPath(),
|
||||
sheetName, sheetPath, pageName, pageNumber,
|
||||
finalPageCount );
|
||||
}
|
||||
|
||||
if (plotter)
|
||||
{
|
||||
plotter->SetTitle(
|
||||
ExpandTextVars( brd->GetTitleBlock().GetTitle(), &textResolver ) );
|
||||
|
||||
if( plotOpts.m_PDFMetadata )
|
||||
{
|
||||
|
||||
msg = wxS( "AUTHOR" );
|
||||
|
||||
if( brd->ResolveTextVar( &msg, 0 ) )
|
||||
plotter->SetAuthor( msg );
|
||||
|
||||
msg = wxS( "SUBJECT" );
|
||||
|
||||
if( brd->ResolveTextVar( &msg, 0 ) )
|
||||
plotter->SetSubject( msg );
|
||||
}
|
||||
|
||||
PlotBoardLayers( brd, plotter, plotSequence, plotOpts );
|
||||
PlotInteractiveLayer( brd, plotter, plotOpts );
|
||||
|
||||
|
||||
if( aPdfJob->m_pdfGenMode == JOB_EXPORT_PCB_PDF::GEN_MODE::ONE_PAGE_PER_LAYER_ONE_FILE
|
||||
&& i != aPdfJob->m_printMaskLayer.size() - 1 )
|
||||
{
|
||||
wxString pageNumber = wxString::Format( "%zu", pageNum + 1 );
|
||||
PCB_LAYER_ID nextLayer = aPdfJob->m_printMaskLayer[i + 1];
|
||||
wxString pageName = brd->GetLayerName( nextLayer );
|
||||
wxString sheetName = layerName;
|
||||
|
||||
static_cast<PDF_PLOTTER*>( plotter )->ClosePage();
|
||||
static_cast<PDF_PLOTTER*>( plotter )->StartPage( pageNumber, pageName );
|
||||
setupPlotterNewPDFPage( plotter, brd, &plotOpts, sheetName, sheetPath,
|
||||
pageNumber, finalPageCount );
|
||||
}
|
||||
|
||||
|
||||
// last page
|
||||
if( aPdfJob->m_pdfGenMode == JOB_EXPORT_PCB_PDF::GEN_MODE::ALL_LAYERS_SEPARATE_FILE
|
||||
|| i == aPdfJob->m_printMaskLayer.size() - 1 )
|
||||
{
|
||||
plotter->EndPlot();
|
||||
delete plotter->RenderSettings();
|
||||
delete plotter;
|
||||
plotter = nullptr;
|
||||
|
||||
msg.Printf( _( "Plotted to '%s'." ), fn.GetFullPath() );
|
||||
m_reporter->Report( msg, RPT_SEVERITY_ACTION );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Printf( _( "Failed to create file '%s'." ), fn.GetFullPath() );
|
||||
m_reporter->Report( msg, RPT_SEVERITY_ERROR );
|
||||
|
||||
returnCode = CLI::EXIT_CODES::ERR_UNKNOWN;
|
||||
|
||||
if( aPdfJob->m_pdfGenMode == JOB_EXPORT_PCB_PDF::GEN_MODE::ONE_PAGE_PER_LAYER_ONE_FILE )
|
||||
{
|
||||
// bail so we dont continue pointlessly
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
returnCode = CLI::EXIT_CODES::ERR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2080,10 +1968,10 @@ void PCBNEW_JOBS_HANDLER::loadOverrideDrawingSheet( BOARD* aBrd, const wxString&
|
||||
|
||||
if( !DS_DATA_MODEL::GetTheInstance().LoadDrawingSheet( filename, &msg ) )
|
||||
{
|
||||
m_reporter->Report( wxString::Format( _( "Error loading drawing sheet '%s'." ),
|
||||
path )
|
||||
+ wxS( "\n" ) + msg + wxS( "\n" ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
m_reporter->Report( wxString::Format( _( "Error loading drawing sheet '%s'." ),
|
||||
path )
|
||||
+ wxS( "\n" ) + msg + wxS( "\n" ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2095,26 +1983,4 @@ void PCBNEW_JOBS_HANDLER::loadOverrideDrawingSheet( BOARD* aBrd, const wxString&
|
||||
|
||||
// failed loading custom path, revert back to default
|
||||
loadSheet( aBrd->GetProject()->GetProjectFile().m_BoardDrawingSheetFile );
|
||||
}
|
||||
|
||||
|
||||
LSEQ PCBNEW_JOBS_HANDLER::getPlotSequence( PCB_LAYER_ID aLayerToPlot, LSEQ aPlotWithAllLayersSeq )
|
||||
{
|
||||
LSEQ plotSequence;
|
||||
|
||||
// Base layer always gets plotted first.
|
||||
plotSequence.push_back( aLayerToPlot );
|
||||
|
||||
for (size_t i = 0; i < aPlotWithAllLayersSeq.size(); i++)
|
||||
{
|
||||
PCB_LAYER_ID layer = aPlotWithAllLayersSeq[i];
|
||||
|
||||
// Don't plot the same layer more than once;
|
||||
if( find( plotSequence.begin(), plotSequence.end(), layer ) != plotSequence.end() )
|
||||
continue;
|
||||
|
||||
plotSequence.push_back( layer );
|
||||
}
|
||||
|
||||
return plotSequence;
|
||||
}
|
@ -59,7 +59,6 @@ private:
|
||||
void loadOverrideDrawingSheet( BOARD* brd, const wxString& aSheetPath );
|
||||
|
||||
DS_PROXY_VIEW_ITEM* getDrawingSheetProxyView( BOARD* aBrd );
|
||||
LSEQ getPlotSequence( PCB_LAYER_ID aLayerToPlot, LSEQ aPlotWithAllLayersSeq );
|
||||
|
||||
BOARD* m_cliBoard;
|
||||
};
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include <build_version.h>
|
||||
#include <gbr_metadata.h>
|
||||
#include <render_settings.h>
|
||||
|
||||
#include <pcb_plotter.h>
|
||||
|
||||
const wxString GetGerberProtelExtension( int aLayer )
|
||||
{
|
||||
@ -379,32 +379,8 @@ void AddGerberX2Attribute( PLOTTER* aPlotter, const BOARD* aBoard, int aLayer,
|
||||
void BuildPlotFileName( wxFileName* aFilename, const wxString& aOutputDir,
|
||||
const wxString& aSuffix, const wxString& aExtension )
|
||||
{
|
||||
// aFilename contains the base filename only (without path and extension)
|
||||
// when calling this function.
|
||||
// It is expected to be a valid filename (this is usually the board filename)
|
||||
aFilename->SetPath( aOutputDir );
|
||||
|
||||
// Set the file extension
|
||||
aFilename->SetExt( aExtension );
|
||||
|
||||
// remove leading and trailing spaces if any from the suffix, if
|
||||
// something survives add it to the name;
|
||||
// also the suffix can contain some not allowed chars in filename (/ \ . : and some others),
|
||||
// so change them to underscore
|
||||
// Remember it can be called from a python script, so the illegal chars
|
||||
// have to be filtered here.
|
||||
wxString suffix = aSuffix;
|
||||
suffix.Trim( true );
|
||||
suffix.Trim( false );
|
||||
|
||||
wxString badchars = wxFileName::GetForbiddenChars(wxPATH_DOS);
|
||||
badchars.Append( "%." );
|
||||
|
||||
for( unsigned ii = 0; ii < badchars.Len(); ii++ )
|
||||
suffix.Replace( badchars[ii], wxT("_") );
|
||||
|
||||
if( !suffix.IsEmpty() )
|
||||
aFilename->SetName( aFilename->GetName() + wxT( "-" ) + suffix );
|
||||
// Kept as compat, incase python junk used it
|
||||
PCB_PLOTTER::BuildPlotFileName( aFilename, aOutputDir, aSuffix, aExtension );
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user