KiCad PCB EDA Suite
pcbplot.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 (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
30 #include <plotter.h>
31 #include <pcbplot.h>
32 #include <base_units.h>
33 #include <locale_io.h>
34 #include <reporter.h>
35 #include <board.h>
36 #include <plotcontroller.h>
37 #include <pcb_plot_params.h>
38 #include <wx/ffile.h>
39 #include <dialog_plot.h>
40 #include <build_version.h>
41 #include <gbr_metadata.h>
42 #include <render_settings.h>
43 
44 
45 const wxString GetGerberProtelExtension( LAYER_NUM aLayer )
46 {
47  if( IsCopperLayer( aLayer ) )
48  {
49  if( aLayer == F_Cu )
50  return wxT( "gtl" );
51  else if( aLayer == B_Cu )
52  return wxT( "gbl" );
53  else
54  {
55  return wxString::Format( wxT( "g%d" ), aLayer+1 );
56  }
57  }
58  else
59  {
60  switch( aLayer )
61  {
62  case B_Adhes: return wxT( "gba" );
63  case F_Adhes: return wxT( "gta" );
64 
65  case B_Paste: return wxT( "gbp" );
66  case F_Paste: return wxT( "gtp" );
67 
68  case B_SilkS: return wxT( "gbo" );
69  case F_SilkS: return wxT( "gto" );
70 
71  case B_Mask: return wxT( "gbs" );
72  case F_Mask: return wxT( "gts" );
73 
74  case Edge_Cuts: return wxT( "gm1" );
75 
76  case Dwgs_User:
77  case Cmts_User:
78  case Eco1_User:
79  case Eco2_User:
80  default: return wxT( "gbr" );
81  }
82  }
83 }
84 
85 
86 const wxString GetGerberFileFunctionAttribute( const BOARD *aBoard, LAYER_NUM aLayer )
87 {
88  wxString attrib;
89 
90  switch( aLayer )
91  {
92  case F_Adhes:
93  attrib = "Glue,Top";
94  break;
95 
96  case B_Adhes:
97  attrib = "Glue,Bot";
98  break;
99 
100  case F_SilkS:
101  attrib = "Legend,Top";
102  break;
103 
104  case B_SilkS:
105  attrib = "Legend,Bot";
106  break;
107 
108  case F_Mask:
109  attrib = "Soldermask,Top";
110  break;
111 
112  case B_Mask:
113  attrib = "Soldermask,Bot";
114  break;
115 
116  case F_Paste:
117  attrib = "Paste,Top";
118  break;
119 
120  case B_Paste:
121  attrib = "Paste,Bot";
122  break;
123 
124  case Edge_Cuts:
125  // Board outline.
126  // Can be "Profile,NP" (Not Plated: usual) or "Profile,P"
127  // This last is the exception (Plated)
128  attrib = "Profile,NP";
129  break;
130 
131  case Dwgs_User:
132  attrib = "OtherDrawing,Comment";
133  break;
134 
135  case Cmts_User:
136  attrib = "Other,Comment";
137  break;
138 
139  case Eco1_User:
140  attrib = "Other,ECO1";
141  break;
142 
143  case Eco2_User:
144  attrib = "Other,ECO2";
145  break;
146 
147  case B_Fab:
148  // This is actually a assembly layer
149  attrib = "AssemblyDrawing,Bot";
150  break;
151 
152  case F_Fab:
153  // This is actually a assembly layer
154  attrib = "AssemblyDrawing,Top";
155  break;
156 
157  case B_Cu:
158  attrib.Printf( wxT( "Copper,L%d,Bot" ), aBoard->GetCopperLayerCount() );
159  break;
160 
161  case F_Cu:
162  attrib = "Copper,L1,Top";
163  break;
164 
165  default:
166  if( IsCopperLayer( aLayer ) )
167  attrib.Printf( wxT( "Copper,L%d,Inr" ), aLayer+1 );
168  else
169  attrib.Printf( wxT( "Other,User" ), aLayer+1 );
170  break;
171  }
172 
173  // This code adds a optional parameter: the type of copper layers.
174  // Because it is not used by Pcbnew (it can be used only by external autorouters)
175  // user do not really set this parameter.
176  // Therefore do not add it.
177  // However, this code is left here, for perhaps a future usage.
178 #if 0
179  // Add the signal type of the layer, if relevant
180  if( IsCopperLayer( aLayer ) )
181  {
182  LAYER_T type = aBoard->GetLayerType( ToLAYER_ID( aLayer ) );
183 
184  switch( type )
185  {
186  case LT_SIGNAL:
187  attrib += ",Signal";
188  break;
189  case LT_POWER:
190  attrib += ",Plane";
191  break;
192  case LT_MIXED:
193  attrib += ",Mixed";
194  break;
195  default:
196  break; // do nothing (but avoid a warning for unhandled LAYER_T values from GCC)
197  }
198  }
199 #endif
200 
201  wxString fileFct;
202  fileFct.Printf( "%%TF.FileFunction,%s*%%", attrib );
203 
204  return fileFct;
205 }
206 
207 
208 static const wxString GetGerberFilePolarityAttribute( LAYER_NUM aLayer )
209 {
210  /* build the string %TF.FilePolarity,Positive*%
211  * or %TF.FilePolarity,Negative*%
212  * an emply string for layers which do not use a polarity
213  *
214  * The value of the .FilePolarity specifies whether the image represents the
215  * presence or absence of material.
216  * This attribute can only be used when the file represents a pattern in a material layer,
217  * e.g. copper, solder mask, legend.
218  * Together with.FileFunction it defines the role of that image in
219  * the layer structure of the PCB.
220  * Note that the .FilePolarity attribute does not change the image -
221  * no attribute does.
222  * It changes the interpretation of the image.
223  * For example, in a copper layer in positive polarity a round flash generates a copper pad.
224  * In a copper layer in negative polarity it generates a clearance.
225  * Solder mask images usually represent solder mask openings and are then negative.
226  * This may be counter-intuitive.
227  */
228  int polarity = 0;
229 
230  switch( aLayer )
231  {
232  case F_Adhes:
233  case B_Adhes:
234  case F_SilkS:
235  case B_SilkS:
236  case F_Paste:
237  case B_Paste:
238  polarity = 1;
239  break;
240 
241  case F_Mask:
242  case B_Mask:
243  polarity = -1;
244  break;
245 
246  default:
247  if( IsCopperLayer( aLayer ) )
248  polarity = 1;
249  break;
250  }
251 
252  wxString filePolarity;
253 
254  if( polarity == 1 )
255  filePolarity = "%TF.FilePolarity,Positive*%";
256  if( polarity == -1 )
257  filePolarity = "%TF.FilePolarity,Negative*%";
258 
259  return filePolarity;
260 }
261 
262 /* Add some X2 attributes to the file header, as defined in the
263  * Gerber file format specification J4 and "Revision 2015.06"
264  */
265 
266 // A helper function to convert a X2 attribute string to a X1 structured comment:
267 static wxString& makeStringCompatX1( wxString& aText, bool aUseX1CompatibilityMode )
268 {
269  if( aUseX1CompatibilityMode )
270  {
271  aText.Replace( "%", "" );
272  aText.Prepend( "G04 #@! " );
273  }
274 
275  return aText;
276 }
277 
278 
279 void AddGerberX2Header( PLOTTER * aPlotter, const BOARD *aBoard, bool aUseX1CompatibilityMode )
280 {
281  wxString text;
282 
283  // Creates the TF,.GenerationSoftware. Format is:
284  // %TF,.GenerationSoftware,<vendor>,<application name>[,<application version>]*%
285  text.Printf( wxT( "%%TF.GenerationSoftware,KiCad,Pcbnew,%s*%%" ), GetBuildVersion() );
286  aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
287 
288  // creates the TF.CreationDate attribute:
289  text = GbrMakeCreationDateAttributeString( aUseX1CompatibilityMode ?
292  aPlotter->AddLineToHeader( text );
293 
294  // Creates the TF,.ProjectId. Format is (from Gerber file format doc):
295  // %TF.ProjectId,<project id>,<project GUID>,<revision id>*%
296  // <project id> is the name of the project, restricted to basic ASCII symbols only,
297  // Rem: <project id> accepts only ASCII 7 code (only basic ASCII codes are allowed in gerber files).
298  // and comma not accepted
299  // All illegal chars will be replaced by underscore
300  //
301  // <project GUID> is a string which is an unique id of a project.
302  // However Kicad does not handle such a project GUID, so it is built from the board name
303  wxFileName fn = aBoard->GetFileName();
304  wxString msg = fn.GetFullName();
305 
306  // Build a <project GUID>, from the board name
307  wxString guid = GbrMakeProjectGUIDfromString( msg );
308 
309  // build the <project id> string: this is the board short filename (without ext)
310  // and all non ASCII chars and comma are replaced by '_'
311  msg = fn.GetName();
312  msg.Replace( wxT( "," ), wxT( "_" ) );
313 
314  // build the <revision id> string. All non ASCII chars and comma are replaced by '_'
315  wxString rev = ExpandTextVars( aBoard->GetTitleBlock().GetRevision(), aBoard->GetProject() );
316  rev.Replace( wxT( "," ), wxT( "_" ) );
317 
318  if( rev.IsEmpty() )
319  rev = wxT( "rev?" );
320 
321  text.Printf( wxT( "%%TF.ProjectId,%s,%s,%s*%%" ), msg.ToAscii(), guid, rev.ToAscii() );
322  aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
323 
324  // Add the TF.SameCoordinates, that specify all gerber files uses the same
325  // origin and orientation, and the registration between files is OK.
326  // The parameter of TF.SameCoordinates is a string that is common
327  // to all files using the same registration and has no special meaning:
328  // this is just a key
329  // Because there is no mirroring/rotation in Kicad, only the plot offset origin
330  // can create incorrect registration.
331  // So we create a key from plot offset options.
332  // and therefore for a given board, all Gerber files having the same key have the same
333  // plot origin and use the same registration
334  //
335  // Currently the key is "Original" when using absolute Pcbnew coordinates,
336  // and te PY ans PY position od auxiliary axis, when using it.
337  // Please, if absolute Pcbnew coordinates, one day, are set by user, change the way
338  // the key is built to ensure file only using the *same* axis have the same key.
339  wxString registration_id = "Original";
340  wxPoint auxOrigin = aBoard->GetDesignSettings().m_AuxOrigin;
341 
342  if( aBoard->GetPlotOptions().GetUseAuxOrigin() && auxOrigin.x && auxOrigin.y )
343  registration_id.Printf( "PX%xPY%x", auxOrigin.x, auxOrigin.y );
344 
345  text.Printf( "%%TF.SameCoordinates,%s*%%", registration_id.GetData() );
346  aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
347 }
348 
349 
350 void AddGerberX2Attribute( PLOTTER * aPlotter,
351  const BOARD *aBoard, LAYER_NUM aLayer, bool aUseX1CompatibilityMode )
352 {
353  AddGerberX2Header( aPlotter, aBoard, aUseX1CompatibilityMode );
354 
355  wxString text;
356 
357  // Add the TF.FileFunction
358  text = GetGerberFileFunctionAttribute( aBoard, aLayer );
359  aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
360 
361  // Add the TF.FilePolarity (for layers which support that)
362  text = GetGerberFilePolarityAttribute( aLayer );
363 
364  if( !text.IsEmpty() )
365  aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
366 }
367 
368 
369 void BuildPlotFileName( wxFileName* aFilename, const wxString& aOutputDir,
370  const wxString& aSuffix, const wxString& aExtension )
371 {
372  // aFilename contains the base filename only (without path and extension)
373  // when calling this function.
374  // It is expected to be a valid filename (this is usually the board filename)
375  aFilename->SetPath( aOutputDir );
376 
377  // Set the file extension
378  aFilename->SetExt( aExtension );
379 
380  // remove leading and trailing spaces if any from the suffix, if
381  // something survives add it to the name;
382  // also the suffix can contain some not allowed chars in filename (/ \ . : and some others),
383  // so change them to underscore
384  // Remember it can be called from a python script, so the illegal chars
385  // have to be filtered here.
386  wxString suffix = aSuffix;
387  suffix.Trim( true );
388  suffix.Trim( false );
389 
390  wxString badchars = wxFileName::GetForbiddenChars(wxPATH_DOS);
391  badchars.Append( "%." );
392 
393  for( unsigned ii = 0; ii < badchars.Len(); ii++ )
394  suffix.Replace( badchars[ii], wxT("_") );
395 
396  if( !suffix.IsEmpty() )
397  aFilename->SetName( aFilename->GetName() + wxT( "-" ) + suffix );
398 }
399 
400 
402 {
403  m_plotter = NULL;
404  m_board = aBoard;
406 }
407 
408 
410 {
411  ClosePlot();
412 }
413 
414 
415 /* IMPORTANT THING TO KNOW: the locale during plots *MUST* be kept as
416  * C/POSIX using a LOCALE_IO object on the stack. This even when
417  * opening/closing the plotfile, since some drivers do I/O even then */
418 
420 {
421  LOCALE_IO toggle;
422 
423  if( m_plotter )
424  {
425  m_plotter->EndPlot();
426 
427  delete m_plotter->RenderSettings();
428  delete m_plotter;
429 
430  m_plotter = NULL;
431  }
432 }
433 
434 
436  const wxString& aSuffix, PLOT_FORMAT aFormat, const wxString& aSheetDesc )
437 {
438  LOCALE_IO toggle;
439 
440  /* Save the current format: sadly some plot routines depends on this
441  but the main reason is that the StartPlot method uses it to
442  dispatch the plotter creation */
443  GetPlotOptions().SetFormat( aFormat );
444 
445  // Ensure that the previous plot is closed
446  ClosePlot();
447 
448  // Now compute the full filename for the output and start the plot
449  // (after ensuring the output directory is OK)
450  wxString outputDirName = GetPlotOptions().GetOutputDirectory() ;
451  wxFileName outputDir = wxFileName::DirName( outputDirName );
452  wxString boardFilename = m_board->GetFileName();
453 
454  if( EnsureFileDirectoryExists( &outputDir, boardFilename ) )
455  {
456  // outputDir contains now the full path of plot files
457  m_plotFile = boardFilename;
458  m_plotFile.SetPath( outputDir.GetPath() );
459  wxString fileExt = GetDefaultPlotExtension( aFormat );
460 
461  // Gerber format can use specific file ext, depending on layers
462  // (now not a good practice, because the official file ext is .gbr)
463  if( GetPlotOptions().GetFormat() == PLOT_FORMAT::GERBER
464  && GetPlotOptions().GetUseGerberProtelExtensions() )
465  fileExt = GetGerberProtelExtension( GetLayer() );
466 
467  // Build plot filenames from the board name and layer names:
468  BuildPlotFileName( &m_plotFile, outputDir.GetPath(), aSuffix, fileExt );
469 
471  m_plotFile.GetFullPath(), aSheetDesc );
472  }
473 
474  return( m_plotter != NULL );
475 }
476 
477 
479 {
480  LOCALE_IO toggle;
481 
482  // No plot open, nothing to do...
483  if( !m_plotter )
484  return false;
485 
486  // Fully delegated to the parent
488 
489  return true;
490 }
491 
492 
493 void PLOT_CONTROLLER::SetColorMode( bool aColorMode )
494 {
495  if( !m_plotter )
496  return;
497 
498  m_plotter->SetColorMode( aColorMode );
499 }
500 
501 
503 {
504  if( !m_plotter )
505  return false;
506 
507  return m_plotter->GetColorMode();
508 }
PCB_PLOT_PARAMS & GetPlotOptions()
Accessor to the plot parameters and options.
LAYER_NUM GetLayer()
Handle special data (items attributes) during plot.
~PLOT_CONTROLLER()
Batch plotter destructor, ensures that the last plot is closed.
Definition: pcbplot.cpp:409
Plot settings, and plotting engines (PostScript, Gerber, HPGL and DXF)
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition: board.h:609
void ClosePlot()
Close the current plot, nothing happens if it isn't open.
Definition: pcbplot.cpp:419
bool OpenPlotfile(const wxString &aSuffix, PLOT_FORMAT aFormat, const wxString &aSheetDesc)
Open a new plotfile; works as a factory for plotter objects.
Definition: pcbplot.cpp:435
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
PLOTTER * m_plotter
This is the plotter object; it starts NULL and become instantiated when a plotfile is requested.
wxString GetDefaultPlotExtension(PLOT_FORMAT aFormat)
Returns the default plot extension for a format.
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:58
Implementation of conversion functions that require both schematic and board internal units.
Definition: board.h:67
void PlotOneBoardLayer(BOARD *aBoard, PLOTTER *aPlotter, PCB_LAYER_ID aLayer, const PCB_PLOT_PARAMS &aPlotOpt)
Function PlotOneBoardLayer main function to plot one copper or technical layer.
virtual void SetColorMode(bool aColorMode)
Plot in B/W or color.
Definition: plotter.h:151
bool GetColorMode()
Definition: pcbplot.cpp:502
PROJECT * GetProject() const
Definition: board.h:430
const wxString GetGerberProtelExtension(LAYER_NUM aLayer)
Function GetGerberProtelExtension.
Definition: pcbplot.cpp:45
void AddGerberX2Header(PLOTTER *aPlotter, const BOARD *aBoard, bool aUseX1CompatibilityMode)
Calculates some X2 attributes, as defined in the Gerber file format specification J4 (chapter 5) and ...
Definition: pcbplot.cpp:279
wxFileName m_plotFile
The current plot filename, set by OpenPlotfile.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.h:591
wxString GbrMakeCreationDateAttributeString(GBR_NC_STRING_FORMAT aFormat)
void AddGerberX2Attribute(PLOTTER *aPlotter, const BOARD *aBoard, LAYER_NUM aLayer, bool aUseX1CompatibilityMode)
Calculates some X2 attributes, as defined in the Gerber file format specification and add them to the...
Definition: pcbplot.cpp:350
void AddLineToHeader(const wxString &aExtraString)
Add a line to the list of free lines to print at the beginning of the file.
Definition: plotter.h:180
const wxString & GetFileName() const
Definition: board.h:298
BOARD * m_board
The board we're plotting.
Definition: board.h:68
PLOT_CONTROLLER(BOARD *aBoard)
Batch plotter constructor, nothing interesting here.
Definition: pcbplot.cpp:401
Board plot function definition file.
virtual bool EndPlot()=0
#define NULL
wxString GetBuildVersion()
Get the full KiCad version string.
const wxString & GetRevision() const
Definition: title_block.h:86
PLOT_FORMAT
The set of supported output plot formats.
Definition: plotter.h:67
wxString GbrMakeProjectGUIDfromString(const wxString &aText)
Build a project GUID using format RFC4122 Version 1 or 4 from the project name, because a KiCad proje...
bool EnsureFileDirectoryExists(wxFileName *aTargetFullFileName, const wxString &aBaseFilename, REPORTER *aReporter)
Make aTargetFullFileName absolute and create the path of this file if it doesn't yet exist.
Definition: common.cpp:297
void SetFormat(PLOT_FORMAT aFormat)
int LAYER_NUM
This can be replaced with int and removed.
wxString GetOutputDirectory() const
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
void BuildPlotFileName(wxFileName *aFilename, const wxString &aOutputDir, const wxString &aSuffix, const wxString &aExtension)
Function BuildPlotFileName (helper function) Complete a plot filename: forces the output directory,...
Definition: pcbplot.cpp:369
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition: board.cpp:376
static const wxString GetGerberFilePolarityAttribute(LAYER_NUM aLayer)
Definition: pcbplot.cpp:208
Base plotter engine class.
Definition: plotter.h:121
void SetColorMode(bool)
Plotters can plot in Black and White mode or Color mode SetColorMode activate/de-actiavte the Color m...
Definition: pcbplot.cpp:493
RENDER_SETTINGS * RenderSettings()
Definition: plotter.h:155
TITLE_BLOCK & GetTitleBlock()
Definition: board.h:612
static wxString & makeStringCompatX1(wxString &aText, bool aUseX1CompatibilityMode)
Definition: pcbplot.cpp:267
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
int GetCopperLayerCount() const
Definition: board.cpp:435
const wxString GetGerberFileFunctionAttribute(const BOARD *aBoard, LAYER_NUM aLayer)
Function GetGerberFileFunctionAttribute Returns the "file function" attribute for aLayer,...
Definition: pcbplot.cpp:86
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
PLOTTER * StartPlotBoard(BOARD *aBoard, PCB_PLOT_PARAMS *aPlotOpts, int aLayer, const wxString &aFullFileName, const wxString &aSheetDesc)
Open a new plotfile using the options (and especially the format) specified in the options and prepar...
bool GetUseAuxOrigin() const
bool PlotLayer()
Plot a single layer on the current plotfile m_plotLayer is the layer to plot.
Definition: pcbplot.cpp:478
LAYER_T
The allowed types of layers, same as Specctra DSN spec.
Definition: board.h:63
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:905
wxPoint m_AuxOrigin
origin for plot exports
LAYER_NUM m_plotLayer
the layer to plot
bool GetColorMode() const
Definition: plotter.h:152