KiCad PCB EDA Suite
gbr_metadata.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) 2018 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
31 #include <fctsys.h>
32 #include <gbr_metadata.h>
33 
35 {
36  // creates the CreationDate attribute:
37  // The attribute value must conform to the full version of the ISO 8601
38  // date and time format, including time and time zone. Note that this is
39  // the date the Gerber file was effectively created,
40  // not the time the project of PCB was started
41  wxDateTime date( wxDateTime::GetTimeNow() );
42  // Date format: see http://www.cplusplus.com/reference/ctime/strftime
43  wxString timezone_offset; // ISO 8601 offset from UTC in timezone
44  timezone_offset = date.Format( "%z" ); // Extract the time zone offset
45  // The time zone offset format is +mm or +hhmm (or -mm or -hhmm)
46  // (mm = number of minutes, hh = number of hours. 1h00mn is returned as +0100)
47  // we want +(or -) hh:mm
48  if( timezone_offset.Len() > 3 ) // format +hhmm or -hhmm found
49  // Add separator between hours and minutes
50  timezone_offset.insert( 3, ":", 1 );
51 
52  wxString msg;
53 
54  switch( aFormat )
55  {
57  msg.Printf( "%%TF.CreationDate,%s%s*%%", date.FormatISOCombined(), timezone_offset );
58  break;
59 
61  msg.Printf( "G04 #@! TF.CreationDate,%s%s", date.FormatISOCombined(), timezone_offset );
62  break;
63 
65  msg.Printf( "\"CreationDate\": \"%s%s\"", date.FormatISOCombined(), timezone_offset );
66  break;
67 
69  msg.Printf( "; #@! TF.CreationDate,%s%s", date.FormatISOCombined(), timezone_offset );
70  break;
71  }
72  return msg;
73 }
74 
75 
76 wxString GbrMakeProjectGUIDfromString( wxString& aText )
77 {
78  /* Gerber GUID format should be RFC4122 Version 1 or 4.
79  * See en.wikipedia.org/wiki/Universally_unique_identifier
80  * The format is:
81  * xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
82  * with
83  * x = hexDigit lower/upper case
84  * and
85  * M = '1' or '4' (UUID version: 1 (basic) or 4 (random)) (we use 4: UUID random)
86  * and
87  * N = '8' or '9' or 'A|a' or 'B|b' : UUID variant 1: 2 MSB bits have meaning) (we use N = 9)
88  * N = 1000 or 1001 or 1010 or 1011 : 10xx means Variant 1 (Variant2: 110x and 111x are reserved)
89  */
90 
91  wxString guid;
92 
93  // Build a 32 digits GUID from the board name:
94  // guid has 32 digits, so add chars in name to be sure we can build a 32 digits guid
95  // (i.e. from a 16 char string name)
96  // In fact only 30 digits are used, and 2 UID id
97  wxString bname = aText;
98  int cnt = 16 - bname.Len();
99 
100  if( cnt > 0 )
101  bname.Append( 'X', cnt );
102 
103  int chr_idx = 0;
104 
105  // Output the 8 first hex digits:
106  for( unsigned ii = 0; ii < 4; ii++ )
107  {
108  int cc = int( bname[chr_idx++] ) & 0xFF;
109  guid << wxString::Format( "%2.2x", cc );
110  }
111 
112  // Output the 4 next hex digits:
113  guid << '-';
114 
115  for( unsigned ii = 0; ii < 2; ii++ )
116  {
117  int cc = int( bname[chr_idx++] ) & 0xFF;
118  guid << wxString::Format( "%2.2x", cc );
119  }
120 
121  // Output the 4 next hex digits (UUID version and 3 digits):
122  guid << "-4"; // first digit: UUID version 4 (M = 4)
123  {
124  int cc = int( bname[chr_idx++] ) << 4 & 0xFF0;
125  cc += int( bname[chr_idx] ) >> 4 & 0x0F;
126  guid << wxString::Format( "%3.3x", cc );
127  }
128 
129  // Output the 4 next hex digits (UUID variant and 3 digits):
130  guid << "-9"; // first digit: UUID variant 1 (N = 9)
131  {
132  int cc = (int( bname[chr_idx++] ) & 0x0F) << 8;
133  cc += int( bname[chr_idx++] ) & 0xFF;
134  guid << wxString::Format( "%3.3x", cc );
135  }
136 
137  // Output the 12 last hex digits:
138  guid << '-';
139 
140  for( unsigned ii = 0; ii < 6; ii++ )
141  {
142  int cc = int( bname[chr_idx++] ) & 0xFF;
143  guid << wxString::Format( "%2.2x", cc );
144  }
145 
146  return guid;
147 }
148 
149 
151  bool aUseX1StructuredComment )
152 {
153  std::string attribute_string;
154 
155  // generate a string to print a Gerber Aperture attribute
156  switch( aAttribute )
157  {
158  case GBR_APERTURE_ATTRIB_END: // Dummy value (aAttribute must be < GBR_APERTURE_ATTRIB_END)
159  case GBR_APERTURE_ATTRIB_NONE: // idle command: do nothing
160  break;
161 
162  case GBR_APERTURE_ATTRIB_ETCHEDCMP: // print info associated to an item
163  // which connects 2 different nets
164  // (Net tees, microwave component)
165  attribute_string = "TA.AperFunction,EtchedComponent";
166  break;
167 
168  case GBR_APERTURE_ATTRIB_CONDUCTOR: // print info associated to a track
169  attribute_string = "TA.AperFunction,Conductor";
170  break;
171 
172  case GBR_APERTURE_ATTRIB_CUTOUT: // print info associated to a outline
173  attribute_string = "TA.AperFunction,CutOut";
174  break;
175 
176  case GBR_APERTURE_ATTRIB_VIAPAD: // print info associated to a flashed via
177  attribute_string = "TA.AperFunction,ViaPad";
178  break;
179 
180  case GBR_APERTURE_ATTRIB_NONCONDUCTOR: // print info associated to a item on a copper layer
181  // which is not a track (for instance a text)
182  attribute_string = "TA.AperFunction,NonConductor";
183  break;
184 
185  case GBR_APERTURE_ATTRIB_COMPONENTPAD: // print info associated to a flashed
186  // through hole component on outer layer
187  attribute_string = "TA.AperFunction,ComponentPad";
188  break;
189 
190  case GBR_APERTURE_ATTRIB_SMDPAD_SMDEF: // print info associated to a flashed for SMD pad.
191  // with solder mask defined from the copper shape
192  // Excluded BGA pads which have their own type
193  attribute_string = "TA.AperFunction,SMDPad,SMDef";
194  break;
195 
196  case GBR_APERTURE_ATTRIB_SMDPAD_CUDEF: // print info associated to a flashed SMD pad with
197  // a solder mask defined by the solder mask
198  attribute_string = "TA.AperFunction,SMDPad,CuDef";
199  break;
200 
201  case GBR_APERTURE_ATTRIB_BGAPAD_SMDEF: // print info associated to flashed BGA pads with
202  // a solder mask defined by the copper shape
203  attribute_string = "TA.AperFunction,BGAPad,SMDef";
204  break;
205 
206  case GBR_APERTURE_ATTRIB_BGAPAD_CUDEF: // print info associated to a flashed BGA pad with
207  // a solder mask defined by the solder mask
208  attribute_string = "TA.AperFunction,BGAPad,CuDef";
209  break;
210 
211  case GBR_APERTURE_ATTRIB_CONNECTORPAD: // print info associated to a flashed edge connector pad (outer layers)
212  attribute_string = "TA.AperFunction,ConnectorPad";
213  break;
214 
215  case GBR_APERTURE_ATTRIB_WASHERPAD: // print info associated to flashed mechanical pads (NPTH)
216  attribute_string = "TA.AperFunction,WasherPad";
217  break;
218 
219  case GBR_APERTURE_ATTRIB_HEATSINKPAD: // print info associated to a flashed heat sink pad
220  // (typically for SMDs)
221  attribute_string = "TA.AperFunction,HeatsinkPad";
222  break;
223 
224  case GBR_APERTURE_ATTRIB_VIADRILL: // print info associated to a via hole in drill files
225  attribute_string = "TA.AperFunction,ViaDrill";
226  break;
227 
228  case GBR_APERTURE_ATTRIB_COMPONENTDRILL: // print info associated to a component
229  // round hole in drill files
230  attribute_string = "TA.AperFunction,ComponentDrill";
231  break;
232 
233  case GBR_APERTURE_ATTRIB_SLOTDRILL: // print info associated to a oblong hole in drill files
234  attribute_string = "TA.AperFunction,Slot";
235  break;
236  }
237 
238  std::string full_attribute_string;
239  wxString eol_string;
240 
241  if( !attribute_string.empty() )
242  {
243  if( aUseX1StructuredComment )
244  {
245  full_attribute_string = "G04 #@! ";
246  eol_string = "*\n";
247  }
248  else
249  {
250  full_attribute_string = "%";
251  eol_string = "*%\n";
252  }
253  }
254 
255  full_attribute_string += attribute_string + eol_string;
256 
257  return full_attribute_string;
258 }
259 
260 wxString FormatStringFromGerber( const wxString& aString )
261 {
262  // make the inverse conversion of formatStringToGerber()
263  // It converts a "normalized" gerber string and convert it to a 16 bits sequence unicode
264  // and return a wxString (unicode 16) from the gerber string
265  wxString txt;
266 
267  for( unsigned ii = 0; ii < aString.Length(); ++ii )
268  {
269  unsigned code = aString[ii];
270 
271  if( code == '\\' )
272  {
273  // Convert 4 hexadecimal digits to a 16 bit unicode
274  // (Gerber allows only 4 hexadecimal digits)
275  long value = 0;
276 
277  for( int jj = 0; jj < 4; jj++ )
278  {
279  value <<= 4;
280  code = aString[++ii];
281  // Very basic conversion, but it expects a valid gerber file
282  int hexa = (code <= '9' ? code - '0' : code - 'A' + 10) & 0xF;
283  value += hexa;
284  }
285 
286  txt.Append( wxChar( value ) );
287  }
288  else
289  txt.Append( aString[ii] );
290  }
291 
292  return txt;
293 }
294 
295 
296 std::string formatStringToGerber( const wxString& aString )
297 {
298  /* format string means convert any code > 0x7F and unautorized code to a hexadecimal
299  * 16 bits sequence unicode
300  * unautorized codes are ',' '*' '%' '\'
301  */
302  std::string txt;
303 
304  txt.reserve( aString.Length() );
305 
306  for( unsigned ii = 0; ii < aString.Length(); ++ii )
307  {
308  unsigned code = aString[ii];
309  bool convert = false;
310 
311  switch( code )
312  {
313  case '\\':
314  case '%':
315  case '*':
316  case ',':
317  convert = true;
318  break;
319 
320  default:
321  break;
322  }
323 
324  if( convert || code > 0x7F )
325  {
326  txt += '\\';
327 
328  // Convert code to 4 hexadecimal digit
329  // (Gerber allows only 4 hexadecimal digit)
330  char hexa[32];
331  sprintf( hexa,"%4.4X", code & 0xFFFF);
332  txt += hexa;
333  }
334  else
335  txt += char( code );
336  }
337 
338  return txt;
339 }
340 
341 // Netname and Pan num fields cannot be empty in Gerber files
342 // Normalized names must be used, if any
343 #define NO_NET_NAME wxT( "N/C" ) // net name of not connected pads (one pad net) (normalized)
344 #define NO_PAD_NAME wxT( "" ) // pad name of pads without pad name/number (not normalized)
345 
346 bool FormatNetAttribute( std::string& aPrintedText, std::string& aLastNetAttributes,
347  GBR_NETLIST_METADATA* aData, bool& aClearPreviousAttributes,
348  bool aUseX1StructuredComment )
349 {
350  aClearPreviousAttributes = false;
351  wxString prepend_string;
352  wxString eol_string;
353 
354  if( aUseX1StructuredComment )
355  {
356  prepend_string = "G04 #@! ";
357  eol_string = "*\n";
358  }
359  else
360  {
361  prepend_string = "%";
362  eol_string = "*%\n";
363  }
364 
365  // print a Gerber net attribute record.
366  // it is added to the object attributes dictionnary
367  // On file, only modified or new attributes are printed.
368  if( aData == NULL )
369  return false;
370 
371  std::string pad_attribute_string;
372  std::string net_attribute_string;
373  std::string cmp_attribute_string;
374 
376  return false; // idle command: do nothing
377 
379  {
380  // print info associated to a flashed pad (cmpref, pad name)
381  // example: %TO.P,R5,3*%
382  pad_attribute_string = prepend_string + "TO.P,";
383  pad_attribute_string += formatStringToGerber( aData->m_Cmpref ) + ",";
384 
385  if( aData->m_Padname.IsEmpty() )
386  // Happens for "mechanical" or never connected pads
387  pad_attribute_string += formatStringToGerber( NO_PAD_NAME );
388  else
389  pad_attribute_string += formatStringToGerber( aData->m_Padname );
390 
391  pad_attribute_string += eol_string;
392  }
393 
395  {
396  // print info associated to a net
397  // example: %TO.N,Clk3*%
398  net_attribute_string = prepend_string + "TO.N,";
399 
400  if( aData->m_Netname.IsEmpty() )
401  {
402  if( aData->m_NotInNet )
403  {
404  // Happens for not connectable pads: mechanical pads
405  // and pads with no padname/num
406  // In this case the net name must be left empty
407  }
408  else
409  {
410  // Happens for not connected pads: use a normalized
411  // dummy name
412  net_attribute_string += formatStringToGerber( NO_NET_NAME );
413  }
414  }
415  else
416  net_attribute_string += formatStringToGerber( aData->m_Netname );
417 
418  net_attribute_string += eol_string;
419  }
420 
423  {
424  // print info associated to a footprint
425  // example: %TO.C,R2*%
426  // Because GBR_NETINFO_PAD option already contains this info, it is not
427  // created here for a GBR_NETINFO_PAD attribute
428  cmp_attribute_string = prepend_string + "TO.C,";
429  cmp_attribute_string += formatStringToGerber( aData->m_Cmpref ) + eol_string;
430  }
431 
432  // the full list of requested attributes:
433  std::string full_attribute_string = pad_attribute_string + net_attribute_string
434  + cmp_attribute_string;
435  // the short list of requested attributes
436  // (only modified or new attributes are stored here):
437  std::string short_attribute_string;
438 
439  if( aLastNetAttributes != full_attribute_string )
440  {
441  // first, remove no more existing attributes.
442  // Because in Kicad the full attribute list is evaluated for each object,
443  // the entire dictionnary is cleared
444  bool clearDict = false;
445 
446  if( aLastNetAttributes.find( "TO.P," ) != std::string::npos )
447  {
448  if( pad_attribute_string.empty() ) // No more this attribute
449  clearDict = true;
450  else if( aLastNetAttributes.find( pad_attribute_string )
451  == std::string::npos ) // This attribute has changed
452  short_attribute_string += pad_attribute_string;
453  }
454  else // New attribute
455  short_attribute_string += pad_attribute_string;
456 
457  if( aLastNetAttributes.find( "TO.N," ) != std::string::npos )
458  {
459  if( net_attribute_string.empty() ) // No more this attribute
460  clearDict = true;
461  else if( aLastNetAttributes.find( net_attribute_string )
462  == std::string::npos ) // This attribute has changed
463  short_attribute_string += net_attribute_string;
464  }
465  else // New attribute
466  short_attribute_string += net_attribute_string;
467 
468  if( aLastNetAttributes.find( "TO.C," ) != std::string::npos )
469  {
470  if( cmp_attribute_string.empty() ) // No more this attribute
471  clearDict = true;
472  else if( aLastNetAttributes.find( cmp_attribute_string )
473  == std::string::npos ) // This attribute has changed
474  short_attribute_string += cmp_attribute_string;
475  }
476  else // New attribute
477  short_attribute_string += cmp_attribute_string;
478 
479  aClearPreviousAttributes = clearDict;
480 
481  aLastNetAttributes = full_attribute_string;
482 
483  if( clearDict )
484  aPrintedText = full_attribute_string;
485  else
486  aPrintedText = short_attribute_string;
487  }
488 
489  return true;
490 }
a class to handle special data (items attributes) during plot.
aperture used for etched components
Definition: gbr_metadata.h:83
#define NO_NET_NAME
aperture used for edge connecto pad (outer layers)
Definition: gbr_metadata.h:93
aperture used for heat sink pad (typically for SMDs)
Definition: gbr_metadata.h:95
print info associated to a component (TO.C attribute)
wxString m_Cmpref
the component reference parent of the data
aperture used for through hole component on outer layer
Definition: gbr_metadata.h:88
aperture used for via holes in drill files
Definition: gbr_metadata.h:96
wxString GbrMakeCreationDateAttributeString(GBR_NC_STRING_FORMAT aFormat)
bool FormatNetAttribute(std::string &aPrintedText, std::string &aLastNetAttributes, GBR_NETLIST_METADATA *aData, bool &aClearPreviousAttributes, bool aUseX1StructuredComment)
Generates the string to print to a gerber file, to set a net attribute for a graphic object...
this class handle info which can be added in a gerber file as attribute of an obtect the GBR_INFO_TYP...
std::string formatStringToGerber(const wxString &aString)
This helper function "normalize" aString and convert it to a Gerber std::string Normalisation means c...
aperture used for SMD pad. Excluded BGA pads which have their own type
Definition: gbr_metadata.h:89
aperture used for BGA pad with a solder mask defined by the solder mask
Definition: gbr_metadata.h:92
wxString m_Padname
for a flashed pad: the pad name ((TO.P attribute)
wxString GbrMakeProjectGUIDfromString(wxString &aText)
A helper function to build a project GUID using format RFC4122 Version 1 or 4 from the project name...
#define NO_PAD_NAME
aperture used for pad holes in drill files
Definition: gbr_metadata.h:97
wxString m_Netname
for items associated to a net: the netname
aperture used for mechanical pads (NPTH)
Definition: gbr_metadata.h:94
aperture used for BGA pads with a solder mask defined by the copper shape
Definition: gbr_metadata.h:91
aperture used for connected items like tracks (not vias)
Definition: gbr_metadata.h:84
static std::string FormatAttribute(GBR_APERTURE_ATTRIB aAttribute, bool aUseX1StructuredComment)
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
aperture used for SMD pad with a solder mask defined by the solder mask
Definition: gbr_metadata.h:90
print info associated to a flashed pad (TO.P attribute)
bool m_NotInNet
true if a pad of a footprint cannot be connected (for instance a mechanical NPTH, ot a not named pad)...
wxString FormatStringFromGerber(const wxString &aString)
This helper function make the inverse conversion of formatStringToGerber() It converts a "normalized"...
aperture used for not connected items (texts, outlines on copper)
Definition: gbr_metadata.h:86
print info associated to a net (TO.N attribute)
GBR_NC_STRING_FORMAT
creates the TF.CreationDate attribute: The attribute value must conform to the full version of the IS...
Definition: gbr_metadata.h:51
aperture used for oblong holes in drill files
Definition: gbr_metadata.h:98
int m_NetAttribType
the type of net info (used to define the gerber string to create)