KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_plotter.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <pcb_plotter.h>
25#include <plotters/plotter.h>
27#include <board.h>
28#include <reporter.h>
29#include <pcbplot.h>
30#include <wx/filename.h>
37
38
39PCB_PLOTTER::PCB_PLOTTER( BOARD* aBoard, REPORTER* aReporter, PCB_PLOT_PARAMS& aParams ) :
40 m_board( aBoard ), m_plotOpts( aParams ), m_reporter( aReporter )
41{
42}
43
44
45bool PCB_PLOTTER::Plot( const wxString& aOutputPath, const LSEQ& aLayersToPlot,
46 const LSEQ& aCommonLayers, bool aUseGerberFileExtensions )
47{
48 std::function<bool( wxString* )> textResolver = [&]( wxString* token ) -> bool
49 {
50 // Handles board->GetTitleBlock() *and* board->GetProject()
51 return m_board->ResolveTextVar( token, 0 );
52 };
53
54 size_t finalPageCount = 0;
55 for( size_t i = 0; i < aLayersToPlot.size(); i++ )
56 {
57 PCB_LAYER_ID layer = aLayersToPlot[i];
58 if( copperLayerShouldBeSkipped( layer ) )
59 continue;
60
61 finalPageCount++;
62 }
63
64 std::unique_ptr<GERBER_JOBFILE_WRITER> jobfile_writer;
65
66 if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER )
67 {
68 jobfile_writer = std::make_unique<GERBER_JOBFILE_WRITER>( m_board, m_reporter );
69 }
70
71 wxString fileExt( GetDefaultPlotExtension( m_plotOpts.GetFormat() ) );
72 wxString sheetPath;
73 PLOTTER* plotter = nullptr;
74 for( size_t i = 0, pageNum = 1; i < aLayersToPlot.size(); i++ )
75 {
76 PCB_LAYER_ID layer = aLayersToPlot[i];
77
78 if( copperLayerShouldBeSkipped( layer ) )
79 continue;
80
81 LSEQ plotSequence = getPlotSequence( layer, aCommonLayers );
82
83 wxString layerName = m_board->GetLayerName( layer );
84
85 wxFileName fn;
86 wxFileName brdFn = m_board->GetFileName();
87 wxString msg;
88 fn.Assign( aOutputPath, brdFn.GetName() );
89
90 // Use Gerber Extensions based on layer number
91 // (See http://en.wikipedia.org/wiki/Gerber_File)
92 if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && aUseGerberFileExtensions )
93 fileExt = GetGerberProtelExtension( layer );
94
95 if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF && m_plotOpts.m_PDFSingle )
96 {
97 fn.SetExt( GetDefaultPlotExtension( PLOT_FORMAT::PDF ) );
98 }
99 else
100 {
101 BuildPlotFileName( &fn, aOutputPath, layerName, fileExt );
102 }
103
104 if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER )
105 {
106 wxString fullname = fn.GetFullName();
107 jobfile_writer->AddGbrFile( layer, fullname );
108 }
109
110 if( m_plotOpts.GetFormat() != PLOT_FORMAT::PDF || !m_plotOpts.m_PDFSingle
111 || ( i == 0 && m_plotOpts.GetFormat() == PLOT_FORMAT::PDF
113 {
114 // this will only be used by pdf
115 wxString pageNumber = wxString::Format( "%zu", pageNum );
116 wxString pageName = layerName;
117 wxString sheetName = layerName;
118
119 plotter = StartPlotBoard( m_board, &m_plotOpts, layer, layerName, fn.GetFullPath(),
120 sheetName,
121 sheetPath, pageName, pageNumber, finalPageCount );
122 }
123
124 if( plotter )
125 {
126 plotter->SetTitle( ExpandTextVars( m_board->GetTitleBlock().GetTitle(), &textResolver ) );
127
129 {
130 msg = wxS( "AUTHOR" );
131
132 if( m_board->ResolveTextVar( &msg, 0 ) )
133 plotter->SetAuthor( msg );
134
135 msg = wxS( "SUBJECT" );
136
137 if( m_board->ResolveTextVar( &msg, 0 ) )
138 plotter->SetSubject( msg );
139 }
140
141 PlotBoardLayers( m_board, plotter, plotSequence, m_plotOpts );
143
144
145 if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF && m_plotOpts.m_PDFSingle
146 && i != aLayersToPlot.size() - 1 )
147 {
148 wxString pageNumber = wxString::Format( "%zu", pageNum + 1 );
149
150 size_t nextI = i;
151 PCB_LAYER_ID nextLayer;
152
153 do
154 {
155 ++nextI;
156 nextLayer = aLayersToPlot[nextI];
157 } while( copperLayerShouldBeSkipped( nextLayer )
158 && ( nextI < aLayersToPlot.size() - 1 ) );
159
160 wxString pageName = m_board->GetLayerName( nextLayer );
161 wxString sheetName = layerName;
162
163 static_cast<PDF_PLOTTER*>( plotter )->ClosePage();
164 static_cast<PDF_PLOTTER*>( plotter )->StartPage( pageNumber, pageName );
165 setupPlotterNewPDFPage( plotter, m_board, &m_plotOpts, sheetName, sheetPath,
166 pageNumber,
167 finalPageCount );
168 }
169
170
171 // last page
172 if( m_plotOpts.GetFormat() != PLOT_FORMAT::PDF || !m_plotOpts.m_PDFSingle
173 || i == aLayersToPlot.size() - 1 )
174 {
175 plotter->EndPlot();
176 delete plotter->RenderSettings();
177 delete plotter;
178 plotter = nullptr;
179
180 msg.Printf( _( "Plotted to '%s'." ), fn.GetFullPath() );
182 }
183 }
184 else
185 {
186 msg.Printf( _( "Failed to create file '%s'." ), fn.GetFullPath() );
188
190 {
191 return false;
192 }
193 }
194
195 pageNum++;
196
197 wxSafeYield(); // displays report message.
198 }
199
200 if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && m_plotOpts.GetCreateGerberJobFile() )
201 {
202 // Pick the basename from the board file
203 wxFileName fn( m_board->GetFileName() );
204
205 // Build gerber job file from basename
206 BuildPlotFileName( &fn, aOutputPath, wxT( "job" ),
208 jobfile_writer->CreateJobFile( fn.GetFullPath() );
209 }
210
212
213 return true;
214}
215
216
218{
219 return ( LSET::AllCuMask() & ~m_board->GetEnabledLayers() )[aLayerToPlot];
220}
221
222
223void PCB_PLOTTER::BuildPlotFileName( wxFileName* aFilename, const wxString& aOutputDir,
224 const wxString& aSuffix, const wxString& aExtension )
225{
226 // aFilename contains the base filename only (without path and extension)
227 // when calling this function.
228 // It is expected to be a valid filename (this is usually the board filename)
229 aFilename->SetPath( aOutputDir );
230
231 // Set the file extension
232 aFilename->SetExt( aExtension );
233
234 // remove leading and trailing spaces if any from the suffix, if
235 // something survives add it to the name;
236 // also the suffix can contain some not allowed chars in filename (/ \ . : and some others),
237 // so change them to underscore
238 // Remember it can be called from a python script, so the illegal chars
239 // have to be filtered here.
240 wxString suffix = aSuffix;
241 suffix.Trim( true );
242 suffix.Trim( false );
243
244 wxString badchars = wxFileName::GetForbiddenChars( wxPATH_DOS );
245 badchars.Append( "%." );
246
247 for( unsigned ii = 0; ii < badchars.Len(); ii++ )
248 suffix.Replace( badchars[ii], wxT( "_" ) );
249
250 if( !suffix.IsEmpty() )
251 aFilename->SetName( aFilename->GetName() + wxT( "-" ) + suffix );
252}
253
254
255LSEQ PCB_PLOTTER::getPlotSequence( PCB_LAYER_ID aLayerToPlot, LSEQ aPlotWithAllLayersSeq )
256{
257 LSEQ plotSequence;
258
259 // Base layer always gets plotted first.
260 plotSequence.push_back( aLayerToPlot );
261
262 for( size_t i = 0; i < aPlotWithAllLayersSeq.size(); i++ )
263 {
264 PCB_LAYER_ID layer = aPlotWithAllLayersSeq[i];
265
266 // Don't plot the same layer more than once;
267 if( find( plotSequence.begin(), plotSequence.end(), layer ) != plotSequence.end() )
268 continue;
269
270 plotSequence.push_back( layer );
271 }
272
273 return plotSequence;
274}
275
276
278{
280 {
281 JOB_EXPORT_PCB_GERBERS* gJob = static_cast<JOB_EXPORT_PCB_GERBERS*>( aJob );
284 aPlotOpts.SetUseGerberX2format( gJob->m_useX2Format );
286 aPlotOpts.SetCreateGerberJobFile( gJob->m_createJobsFile );
287 aPlotOpts.SetGerberPrecision( gJob->m_precision );
289 }
290
292 {
293 JOB_EXPORT_PCB_SVG* svgJob = static_cast<JOB_EXPORT_PCB_SVG*>( aJob );
294 aPlotOpts.SetSvgPrecision( svgJob->m_precision );
295 }
296
298 {
299 JOB_EXPORT_PCB_DXF* dxfJob = static_cast<JOB_EXPORT_PCB_DXF*>( aJob );
301 ? DXF_UNITS::INCHES
302 : DXF_UNITS::MILLIMETERS );
303
304 aPlotOpts.SetPlotMode( dxfJob->m_plotGraphicItemsUsingContours ? OUTLINE_MODE::SKETCH
305 : OUTLINE_MODE::FILLED );
306
307 aPlotOpts.SetDXFPlotPolygonMode( dxfJob->m_polygonMode );
308 }
309
311 {
312 JOB_EXPORT_PCB_PDF* pdfJob = static_cast<JOB_EXPORT_PCB_PDF*>( aJob );
315 aPlotOpts.m_PDFMetadata = pdfJob->m_pdfMetadata;
316 aPlotOpts.m_PDFSingle = pdfJob->m_pdfSingle;
317 }
318
319 aPlotOpts.SetUseAuxOrigin( aJob->m_useDrillOrigin );
320 aPlotOpts.SetPlotFrameRef( aJob->m_plotDrawingSheet );
321 aPlotOpts.SetPlotInvisibleText( aJob->m_plotInvisibleText );
326 aPlotOpts.SetPlotPadNumbers( aJob->m_plotPadNumbers );
327
328 aPlotOpts.SetBlackAndWhite( aJob->m_blackAndWhite );
329 aPlotOpts.SetMirror( aJob->m_mirror );
330 aPlotOpts.SetNegative( aJob->m_negative );
331
332 aPlotOpts.SetLayerSelection( aJob->m_printMaskLayer );
334
335 switch( aJob->m_plotFormat )
336 {
337 default:
339 aPlotOpts.SetFormat( PLOT_FORMAT::GERBER );
340 break;
341 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::POST: aPlotOpts.SetFormat( PLOT_FORMAT::POST ); break;
342 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::SVG: aPlotOpts.SetFormat( PLOT_FORMAT::SVG ); break;
343 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::DXF: aPlotOpts.SetFormat( PLOT_FORMAT::DXF ); break;
344 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::HPGL: aPlotOpts.SetFormat( PLOT_FORMAT::HPGL ); break;
345 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::PDF: aPlotOpts.SetFormat( PLOT_FORMAT::PDF ); break;
346 }
347
348 switch( aJob->m_drillShapeOption )
349 {
351 aPlotOpts.SetDrillMarksType( DRILL_MARKS::NO_DRILL_SHAPE );
352 break;
354 aPlotOpts.SetDrillMarksType( DRILL_MARKS::SMALL_DRILL_SHAPE );
355 break;
356 default:
358 aPlotOpts.SetDrillMarksType( DRILL_MARKS::FULL_DRILL_SHAPE );
359 break;
360 }
361}
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:290
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:816
bool ResolveTextVar(wxString *token, int aDepth) const
Definition: board.cpp:432
TITLE_BLOCK & GetTitleBlock()
Definition: board.h:698
const wxString & GetFileName() const
Definition: board.h:327
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:612
bool m_pdfSingle
This is a hack to deal with cli having the wrong behavior We will deprecate out the wrong behavior,...
DRILL_MARKS m_drillShapeOption
Used by SVG/DXF/PDF/Gerbers.
LSEQ m_printMaskLayer
Layers to include on all individual layer prints.
bool m_mirror
Common Options.
unsigned int m_precision
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: lseq.h:47
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:686
LSEQ getPlotSequence(PCB_LAYER_ID aLayerToPlot, LSEQ aPlotWithAllLayersSeq)
Generates a final LSEQ for plotting by removing duplicates.
bool Plot(const wxString &aOutputPath, const LSEQ &aLayersToPlot, const LSEQ &aCommonLayers, bool aUseGerberFileExtensions)
Definition: pcb_plotter.cpp:45
BOARD * m_board
Definition: pcb_plotter.h:72
static void PlotJobToPlotOpts(PCB_PLOT_PARAMS &aPlotOpts, JOB_EXPORT_PCB_PLOT *aJob)
Translate a JOB to PCB_PLOT_PARAMS.
PCB_PLOTTER(BOARD *aBoard, REPORTER *aReporter, PCB_PLOT_PARAMS &aParams)
Definition: pcb_plotter.cpp:39
PCB_PLOT_PARAMS m_plotOpts
Definition: pcb_plotter.h:73
REPORTER * m_reporter
Definition: pcb_plotter.h:74
bool copperLayerShouldBeSkipped(PCB_LAYER_ID aLayerToPlot)
All copper layers that are disabled are actually selected This is due to wonkyness in automatically s...
static void BuildPlotFileName(wxFileName *aFilename, const wxString &aOutputDir, const wxString &aSuffix, const wxString &aExtension)
Complete a plot filename.
Parameters and options when plotting/printing a board.
PLOT_FORMAT GetFormat() const
void SetDrillMarksType(DRILL_MARKS aVal)
void SetLayerSelection(LSET aSelection)
void SetSketchPadsOnFabLayers(bool aFlag)
void SetUseGerberX2format(bool aUse)
void SetDXFPlotPolygonMode(bool aFlag)
void SetPlotFrameRef(bool aFlag)
void SetSketchDNPFPsOnFabLayers(bool aFlag)
bool m_PDFMetadata
Generate PDF metadata for SUBJECT and AUTHOR.
bool GetCreateGerberJobFile() const
void SetPlotPadNumbers(bool aFlag)
void SetPlotOnAllLayersSelection(LSET aSelection)
bool m_PDFFrontFPPropertyPopups
Generate PDF property popup menus for footprints.
void SetDisableGerberMacros(bool aDisable)
void SetMirror(bool aFlag)
void SetBlackAndWhite(bool blackAndWhite)
void SetGerberPrecision(int aPrecision)
void SetSubtractMaskFromSilk(bool aSubtract)
void SetHideDNPFPsOnFabLayers(bool aFlag)
void SetUseGerberProtelExtensions(bool aUse)
void SetDXFPlotUnits(DXF_UNITS aUnit)
void SetIncludeGerberNetlistInfo(bool aUse)
void SetPlotInvisibleText(bool aFlag)
void SetCreateGerberJobFile(bool aCreate)
bool m_PDFSingle
Generate a single PDF file for all layers.
void SetNegative(bool aFlag)
void SetPlotMode(OUTLINE_MODE aPlotMode)
void SetUseAuxOrigin(bool aAux)
bool m_PDFBackFPPropertyPopups
on front and/or back of board
void SetSvgPrecision(unsigned aPrecision)
void SetCrossoutDNPFPsOnFabLayers(bool aFlag)
void SetFormat(PLOT_FORMAT aFormat)
Base plotter engine class.
Definition: plotter.h:105
virtual void SetAuthor(const wxString &aAuthor)
Definition: plotter.h:156
virtual void SetTitle(const wxString &aTitle)
Definition: plotter.h:155
virtual bool EndPlot()=0
RENDER_SETTINGS * RenderSettings()
Definition: plotter.h:136
virtual void SetSubject(const wxString &aSubject)
Definition: plotter.h:157
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:72
virtual REPORTER & ReportTail(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Places the report at the end of the list, for objects that support report ordering.
Definition: reporter.h:100
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
const wxString & GetTitle() const
Definition: title_block.h:63
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition: common.cpp:59
wxString GetDefaultPlotExtension(PLOT_FORMAT aFormat)
Returns the default plot extension for a format.
#define _(s)
Classes used to generate a Gerber job file in JSON.
static const std::string GerberJobFileExtension
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
const wxString GetGerberProtelExtension(int aLayer)
Definition: pcbplot.cpp:43
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 &aPageName=wxT("1"), const wxString &aPageNumber=wxEmptyString, const int aPageCount=1)
Open a new plotfile using the options (and especially the format) specified in the options and prepar...
void setupPlotterNewPDFPage(PLOTTER *aPlotter, BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, const wxString &aSheetName, const wxString &aSheetPath, const wxString &aPageNumber, int aPageCount)
void PlotBoardLayers(BOARD *aBoard, PLOTTER *aPlotter, const LSEQ &aLayerSequence, const PCB_PLOT_PARAMS &aPlotOptions)
Plot a sequence of board layer IDs.
void PlotInteractiveLayer(BOARD *aBoard, PLOTTER *aPlotter, const PCB_PLOT_PARAMS &aPlotOpt)
Plot interactive items (hypertext links, properties, etc.).
Plotting engines similar to ps (PostScript, Gerber, svg)
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION