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#include <pgm_base.h>
38#include <pcbnew_settings.h>
39
40
41PCB_PLOTTER::PCB_PLOTTER( BOARD* aBoard, REPORTER* aReporter, PCB_PLOT_PARAMS& aParams ) :
42 m_board( aBoard ),
43 m_plotOpts( aParams ),
44 m_reporter( aReporter )
45{
46}
47
48
49bool PCB_PLOTTER::Plot( const wxString& aOutputPath,
50 const LSEQ& aLayersToPlot,
51 const LSEQ& aCommonLayers,
52 bool aUseGerberFileExtensions,
53 bool aOutputPathIsSingle,
54 std::optional<wxString> aLayerName,
55 std::optional<wxString> aSheetName,
56 std::optional<wxString> aSheetPath )
57{
58 std::function<bool( wxString* )> textResolver = [&]( wxString* token ) -> bool
59 {
60 // Handles board->GetTitleBlock() *and* board->GetProject()
61 return m_board->ResolveTextVar( token, 0 );
62 };
63
64 // sanity, ensure one layer to print
65 if( aLayersToPlot.size() < 1 )
66 {
67 m_reporter->Report( _( "No layers selected for plotting." ), RPT_SEVERITY_ERROR );
68 return false;
69 }
70
71 // To reuse logic, in single plot mode, we want to kick any extra layers from the main list to commonLayers
72 LSEQ layersToPlot;
73 LSEQ commonLayers;
74
75 if( aOutputPathIsSingle )
76 {
77 layersToPlot.push_back( aLayersToPlot[0] );
78
79 if( aLayersToPlot.size() > 1 )
80 {
81 commonLayers.insert( commonLayers.end(), aLayersToPlot.begin() + 1,
82 aLayersToPlot.end() );
83 }
84 }
85 else
86 {
87 layersToPlot = aLayersToPlot;
88 commonLayers = aCommonLayers;
89 }
90
91 size_t finalPageCount = 0;
92
93 for( PCB_LAYER_ID layer : layersToPlot )
94 {
95 if( copperLayerShouldBeSkipped( layer ) )
96 continue;
97
98 finalPageCount++;
99 }
100
101 std::unique_ptr<GERBER_JOBFILE_WRITER> jobfile_writer;
102
103 if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && !aOutputPathIsSingle )
104 jobfile_writer = std::make_unique<GERBER_JOBFILE_WRITER>( m_board, m_reporter );
105
106 wxString fileExt( GetDefaultPlotExtension( m_plotOpts.GetFormat() ) );
107 wxString sheetPath;
108 wxString msg;
109 bool success = true;
110 PLOTTER* plotter = nullptr;
111
112 for( size_t i = 0, pageNum = 1; i < layersToPlot.size(); i++ )
113 {
114 PCB_LAYER_ID layer = layersToPlot[i];
115
116 if( copperLayerShouldBeSkipped( layer ) )
117 continue;
118
119 LSEQ plotSequence = getPlotSequence( layer, commonLayers );
120
121 wxString layerName = m_board->GetLayerName( layer );
122
123 wxFileName fn;
124
125 if( aOutputPathIsSingle )
126 {
127 fn = wxFileName( aOutputPath );
128 }
129 else
130 {
131 wxFileName brdFn = m_board->GetFileName();
132 fn.Assign( aOutputPath, brdFn.GetName() );
133
134 // Use Gerber Extensions based on layer number
135 // (See http://en.wikipedia.org/wiki/Gerber_File)
136 if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && aUseGerberFileExtensions )
137 fileExt = GetGerberProtelExtension( layer );
138
139 if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF && m_plotOpts.m_PDFSingle )
140 {
141 fn.SetExt( GetDefaultPlotExtension( PLOT_FORMAT::PDF ) );
142 }
143 else
144 {
145 BuildPlotFileName( &fn, aOutputPath, layerName, fileExt );
146 }
147 }
148
149 if( jobfile_writer )
150 {
151 wxString fullname = fn.GetFullName();
152 jobfile_writer->AddGbrFile( layer, fullname );
153 }
154
155 if( m_plotOpts.GetFormat() != PLOT_FORMAT::PDF
157 || ( pageNum == 1 && m_plotOpts.GetFormat() == PLOT_FORMAT::PDF
159 {
160 // this will only be used by pdf
161 wxString pageNumber = wxString::Format( "%zu", pageNum );
162 wxString pageName = layerName;
163 wxString sheetName = layerName;
164
165 if( aLayerName.has_value() )
166 {
167 layerName = aLayerName.value();
168 pageName = aLayerName.value();
169 }
170
171 if( aSheetName.has_value() )
172 sheetName = aSheetName.value();
173
174 if( aSheetPath.has_value() )
175 sheetPath = aSheetPath.value();
176
177 plotter = StartPlotBoard( m_board, &m_plotOpts, layer, layerName, fn.GetFullPath(),
178 sheetName, sheetPath, pageName, pageNumber, finalPageCount );
179 }
180
181 if( plotter )
182 {
183 plotter->SetTitle( ExpandTextVars( m_board->GetTitleBlock().GetTitle(), &textResolver ) );
184
186 {
187 msg = wxS( "AUTHOR" );
188
189 if( m_board->ResolveTextVar( &msg, 0 ) )
190 plotter->SetAuthor( msg );
191
192 msg = wxS( "SUBJECT" );
193
194 if( m_board->ResolveTextVar( &msg, 0 ) )
195 plotter->SetSubject( msg );
196 }
197
198 PlotBoardLayers( m_board, plotter, plotSequence, m_plotOpts );
200
201
202 if( m_plotOpts.GetFormat() == PLOT_FORMAT::PDF && m_plotOpts.m_PDFSingle
203 && i != layersToPlot.size() - 1 )
204 {
205 wxString pageNumber = wxString::Format( "%zu", pageNum + 1 );
206 size_t nextI = i;
207 PCB_LAYER_ID nextLayer;
208
209 do
210 {
211 ++nextI;
212 nextLayer = layersToPlot[nextI];
213 } while( copperLayerShouldBeSkipped( nextLayer )
214 && ( nextI < layersToPlot.size() - 1 ) );
215
216 wxString pageName = m_board->GetLayerName( nextLayer );
217 wxString sheetName = layerName;
218
219 static_cast<PDF_PLOTTER*>( plotter )->ClosePage();
220 static_cast<PDF_PLOTTER*>( plotter )->StartPage( pageNumber, pageName );
221 setupPlotterNewPDFPage( plotter, m_board, &m_plotOpts, sheetName, sheetPath,
222 pageNumber, finalPageCount );
223 }
224
225
226 // last page
227 if( m_plotOpts.GetFormat() != PLOT_FORMAT::PDF
229 || i == aLayersToPlot.size() - 1
230 || pageNum == finalPageCount )
231 {
232 plotter->EndPlot();
233 delete plotter->RenderSettings();
234 delete plotter;
235 plotter = nullptr;
236
237 msg.Printf( _( "Plotted to '%s'." ), fn.GetFullPath() );
239 }
240 }
241 else
242 {
243 msg.Printf( _( "Failed to create file '%s'." ), fn.GetFullPath() );
245
246 success = false;
247 }
248
249 pageNum++;
250
251 wxSafeYield(); // displays report message.
252 }
253
254 if( jobfile_writer && m_plotOpts.GetCreateGerberJobFile() )
255 {
256 // Pick the basename from the board file
257 wxFileName fn( m_board->GetFileName() );
258
259 // Build gerber job file from basename
260 BuildPlotFileName( &fn, aOutputPath, wxT( "job" ), FILEEXT::GerberJobFileExtension );
261 jobfile_writer->CreateJobFile( fn.GetFullPath() );
262 }
263
265
266 return success;
267}
268
269
271{
272 return ( LSET::AllCuMask() & ~m_board->GetEnabledLayers() )[aLayerToPlot];
273}
274
275
276void PCB_PLOTTER::BuildPlotFileName( wxFileName* aFilename, const wxString& aOutputDir,
277 const wxString& aSuffix, const wxString& aExtension )
278{
279 // aFilename contains the base filename only (without path and extension)
280 // when calling this function.
281 // It is expected to be a valid filename (this is usually the board filename)
282 aFilename->SetPath( aOutputDir );
283
284 // Set the file extension
285 aFilename->SetExt( aExtension );
286
287 // remove leading and trailing spaces if any from the suffix, if
288 // something survives add it to the name;
289 // also the suffix can contain some not allowed chars in filename (/ \ . : and some others),
290 // so change them to underscore
291 // Remember it can be called from a python script, so the illegal chars
292 // have to be filtered here.
293 wxString suffix = aSuffix;
294 suffix.Trim( true );
295 suffix.Trim( false );
296
297 wxString badchars = wxFileName::GetForbiddenChars( wxPATH_DOS );
298 badchars.Append( "%." );
299
300 for( unsigned ii = 0; ii < badchars.Len(); ii++ )
301 suffix.Replace( badchars[ii], wxT( "_" ) );
302
303 if( !suffix.IsEmpty() )
304 aFilename->SetName( aFilename->GetName() + wxT( "-" ) + suffix );
305}
306
307
308LSEQ PCB_PLOTTER::getPlotSequence( PCB_LAYER_ID aLayerToPlot, LSEQ aPlotWithAllLayersSeq )
309{
310 LSEQ plotSequence;
311
312 // Base layer always gets plotted first.
313 plotSequence.push_back( aLayerToPlot );
314
315 for( PCB_LAYER_ID layer : aPlotWithAllLayersSeq )
316 {
317 // Don't plot the same layer more than once;
318 if( find( plotSequence.begin(), plotSequence.end(), layer ) != plotSequence.end() )
319 continue;
320
321 plotSequence.push_back( layer );
322 }
323
324 return plotSequence;
325}
326
327
329 REPORTER& aReporter )
330{
332 {
333 JOB_EXPORT_PCB_GERBERS* gJob = static_cast<JOB_EXPORT_PCB_GERBERS*>( aJob );
336 aOpts.SetUseGerberX2format( gJob->m_useX2Format );
339 aOpts.SetGerberPrecision( gJob->m_precision );
341 }
342
344 {
345 JOB_EXPORT_PCB_SVG* svgJob = static_cast<JOB_EXPORT_PCB_SVG*>( aJob );
346 aOpts.SetSvgPrecision( svgJob->m_precision );
347 }
348
350 {
351 JOB_EXPORT_PCB_DXF* dxfJob = static_cast<JOB_EXPORT_PCB_DXF*>( aJob );
353 ? DXF_UNITS::INCHES
354 : DXF_UNITS::MILLIMETERS );
355
356 aOpts.SetPlotMode( dxfJob->m_plotGraphicItemsUsingContours ? OUTLINE_MODE::SKETCH
357 : OUTLINE_MODE::FILLED );
358
359 aOpts.SetDXFPlotPolygonMode( dxfJob->m_polygonMode );
360 }
361
363 {
364 JOB_EXPORT_PCB_PDF* pdfJob = static_cast<JOB_EXPORT_PCB_PDF*>( aJob );
367 aOpts.m_PDFMetadata = pdfJob->m_pdfMetadata;
368 aOpts.m_PDFSingle = pdfJob->m_pdfSingle;
369 }
370
371 aOpts.SetUseAuxOrigin( aJob->m_useDrillOrigin );
372 aOpts.SetPlotFrameRef( aJob->m_plotDrawingSheet );
378 aOpts.SetPlotPadNumbers( aJob->m_plotPadNumbers );
379
380 aOpts.SetBlackAndWhite( aJob->m_blackAndWhite );
381 aOpts.SetMirror( aJob->m_mirror );
382 aOpts.SetNegative( aJob->m_negative );
383
384 aOpts.SetLayerSelection( aJob->m_printMaskLayer );
386
387 switch( aJob->m_plotFormat )
388 {
389 default:
390 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::GERBER: aOpts.SetFormat( PLOT_FORMAT::GERBER ); break;
391 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::POST: aOpts.SetFormat( PLOT_FORMAT::POST ); break;
392 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::SVG: aOpts.SetFormat( PLOT_FORMAT::SVG ); break;
393 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::DXF: aOpts.SetFormat( PLOT_FORMAT::DXF ); break;
394 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::HPGL: aOpts.SetFormat( PLOT_FORMAT::HPGL ); break;
395 case JOB_EXPORT_PCB_PLOT::PLOT_FORMAT::PDF: aOpts.SetFormat( PLOT_FORMAT::PDF ); break;
396 }
397
398 switch( aJob->m_drillShapeOption )
399 {
401 aOpts.SetDrillMarksType( DRILL_MARKS::NO_DRILL_SHAPE );
402 break;
404 aOpts.SetDrillMarksType( DRILL_MARKS::SMALL_DRILL_SHAPE );
405 break;
406 default:
408 aOpts.SetDrillMarksType( DRILL_MARKS::FULL_DRILL_SHAPE );
409 break;
410 }
411
413 wxString theme = aJob->m_colorTheme;
414
415 // Theme may be empty when running from a job in GUI context, so use the GUI settings.
416 if( theme.IsEmpty() )
417 {
418 PCBNEW_SETTINGS* pcbSettings = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" );
419 theme = pcbSettings->m_ColorTheme;
420 }
421
422 COLOR_SETTINGS* colors = mgr.GetColorSettings( aJob->m_colorTheme );
423
424 if( colors->GetFilename() != theme )
425 {
426 aReporter.Report( wxString::Format(
427 _( "Color theme '%s' not found, will use theme from PCB Editor settings.\n" ),
428 theme ),
430 }
431
432 aOpts.SetColorSettings( colors );
434}
wxString m_ColorTheme
Active color theme name.
Definition: app_settings.h:199
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:295
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:831
bool ResolveTextVar(wxString *token, int aDepth) const
Definition: board.cpp:436
TITLE_BLOCK & GetTitleBlock()
Definition: board.h:706
const wxString & GetFileName() const
Definition: board.h:332
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:616
Color settings are a bit different than most of the settings objects in that there can be more than o...
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
wxString GetConfiguredOutputPath() const
Returns the configured output path for the job.
Definition: job.h:226
wxString GetFilename() const
Definition: json_settings.h:80
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:564
LSEQ getPlotSequence(PCB_LAYER_ID aLayerToPlot, LSEQ aPlotWithAllLayersSeq)
Generates a final LSEQ for plotting by removing duplicates.
BOARD * m_board
Definition: pcb_plotter.h:77
bool Plot(const wxString &aOutputPath, const LSEQ &aLayersToPlot, const LSEQ &aCommonLayers, bool aUseGerberFileExtensions, bool aOutputPathIsSingle=false, std::optional< wxString > aLayerName=std::nullopt, std::optional< wxString > aSheetName=std::nullopt, std::optional< wxString > aSheetPath=std::nullopt)
Definition: pcb_plotter.cpp:49
static void PlotJobToPlotOpts(PCB_PLOT_PARAMS &aOpts, JOB_EXPORT_PCB_PLOT *aJob, REPORTER &aReporter)
Translate a JOB to PCB_PLOT_PARAMS.
PCB_PLOTTER(BOARD *aBoard, REPORTER *aReporter, PCB_PLOT_PARAMS &aParams)
Definition: pcb_plotter.cpp:41
PCB_PLOT_PARAMS m_plotOpts
Definition: pcb_plotter.h:78
REPORTER * m_reporter
Definition: pcb_plotter.h:79
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 SetOutputDirectory(const wxString &aDir)
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 SetColorSettings(COLOR_SETTINGS *aSettings)
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)
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:125
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.
COLOR_SETTINGS * GetColorSettings(const wxString &aName="user")
Retrieve a color settings object that applications can read colors from.
T * GetAppSettings(const wxString &aFilename)
Return a handle to the a given settings by type.
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)
Return 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.).
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
Plotting engines similar to ps (PostScript, Gerber, svg)
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION