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