KiCad PCB EDA Suite
gbr_metadata.cpp File Reference

helper functions to handle the gerber metadata in files, related to the netlist info and aperture attribute. More...

#include <wx/string.h>
#include <wx/datetime.h>
#include <gbr_metadata.h>
#include <utf8.h>

Go to the source code of this file.

Macros

#define NO_NET_NAME   wxT( "N/C" )
 
#define NO_PAD_NAME   wxT( "" )
 

Functions

wxString GbrMakeCreationDateAttributeString (GBR_NC_STRING_FORMAT aFormat)
 
wxString GbrMakeProjectGUIDfromString (const wxString &aText)
 Build a project GUID using format RFC4122 Version 1 or 4 from the project name, because a KiCad project has no specific GUID. More...
 
int char2Hex (unsigned aCode)
 
wxString FormatStringFromGerber (const wxString &aString)
 Convert a gerber string into a 16 bit Unicode string. More...
 
wxString ConvertNotAllowedCharsInGerber (const wxString &aString, bool aAllowUtf8Chars, bool aQuoteString)
 Normalize aString and convert it to a Gerber compatible wxString. More...
 
std::string FormatStringToGerber (const wxString &aString)
 Normalize aString and convert it to a Gerber std::string. More...
 
bool FormatNetAttribute (std::string &aPrintedText, std::string &aLastNetAttributes, const GBR_NETLIST_METADATA *aData, bool &aClearPreviousAttributes, bool aUseX1StructuredComment)
 Generate the string to set a net attribute for a graphic object to print to a gerber file. More...
 

Detailed Description

helper functions to handle the gerber metadata in files, related to the netlist info and aperture attribute.

Definition in file gbr_metadata.cpp.

Macro Definition Documentation

◆ NO_NET_NAME

#define NO_NET_NAME   wxT( "N/C" )

Definition at line 538 of file gbr_metadata.cpp.

◆ NO_PAD_NAME

#define NO_PAD_NAME   wxT( "" )

Definition at line 539 of file gbr_metadata.cpp.

Function Documentation

◆ char2Hex()

int char2Hex ( unsigned  aCode)

Definition at line 354 of file gbr_metadata.cpp.

355 {
356  if( aCode >= '0' && aCode <= '9' )
357  return aCode - '0';
358 
359  if( aCode >= 'A' && aCode <= 'F' )
360  return aCode - 'A' + 10;
361 
362  if( aCode >= 'a' && aCode <= 'f' )
363  return aCode - 'a' + 10;
364 
365  return -1;
366 }

Referenced by FormatStringFromGerber().

◆ ConvertNotAllowedCharsInGerber()

wxString ConvertNotAllowedCharsInGerber ( const wxString &  aString,
bool  aAllowUtf8Chars,
bool  aQuoteString 
)

Normalize aString and convert it to a Gerber compatible wxString.

Normalization means convert to a hexadecimal 16 bit sequence Unicode and on request convert any code > 0x7F. Illegal characters are ',' '*' '' '\'.

Parameters
aStringthe string to convert.
aAllowUtf8Charsfalse to convert non ASCII7 values to Unicode sequence.
aQuoteStringtrue to double quote the returned string.
Returns
a without illegal chars (and converted non ASCII7 chars on request)

Definition at line 435 of file gbr_metadata.cpp.

437 {
438  /* format string means convert any code > 0x7E and unauthorized codes to a hexadecimal
439  * 16 bits sequence Unicode
440  * However if aAllowUtf8Chars is true only unauthorized codes will be escaped, because some
441  * Gerber files accept UTF8 chars.
442  * unauthorized codes are ',' '*' '%' '\' '"' and are used as separators in Gerber files
443  */
444  wxString txt;
445 
446  if( aQuoteString )
447  txt << "\"";
448 
449  for( unsigned ii = 0; ii < aString.Length(); ++ii )
450  {
451  wxChar code = aString[ii];
452  bool convert = false;
453 
454  switch( code )
455  {
456  case '\\':
457  case '%':
458  case '*':
459  case ',':
460  convert = true;
461  break;
462 
463  case '"':
464  if( aQuoteString )
465  convert = true;
466  break;
467 
468  default:
469  break;
470  }
471 
472  if( !aAllowUtf8Chars && code > 0x7F )
473  convert = true;
474 
475  if( convert )
476  {
477  // Convert code to 4 hexadecimal digit
478  // (Gerber allows only 4 hexadecimal digit) in escape seq:
479  // "\uXXXX", XXXX is the Unicode 16 bits hexa value
480  char hexa[32];
481  sprintf( hexa,"\\u%4.4X", code & 0xFFFF);
482  txt += hexa;
483  }
484  else
485  {
486  txt += code;
487  }
488  }
489 
490  if( aQuoteString )
491  txt << "\"";
492 
493  return txt;
494 }

References convert.

Referenced by PLACEFILE_GERBER_WRITER::CreatePlaceFile(), FormatStringToGerber(), and GBR_DATA_FIELD::GetGerberString().

◆ FormatNetAttribute()

bool FormatNetAttribute ( std::string &  aPrintedText,
std::string &  aLastNetAttributes,
const GBR_NETLIST_METADATA aData,
bool &  aClearPreviousAttributes,
bool  aUseX1StructuredComment 
)

Generate the string to set a net attribute for a graphic object to print to a gerber file.

Parameters
aPrintedTextis the string to print.
aLastNetAttributesis the current full set of attributes.
aDatais the GBR_NETLIST_METADATA associated to the graphic object (can be NULL if no associated metadata, and aClearPreviousAttributes will be set to false)
aClearPreviousAttributesreturns true if the full set of attributes must be deleted from file before adding new attribute (happens when a previous attribute no longer exists).
aUseX1StructuredCommentfalse in X2 mode and true in X1 mode to add the net attribute in compatible X1 structured comment (i.e. prefixed by "G04 #@! ")
Returns
false if nothing can be done (GBR_NETLIST_METADATA has GBR_APERTURE_ATTRIB_NONE, and true if OK. If the new attribute(s) is the same as current attribute(s), aPrintedText will be empty.

Definition at line 542 of file gbr_metadata.cpp.

545 {
546  aClearPreviousAttributes = false;
547  wxString prepend_string;
548  wxString eol_string;
549 
550  if( aUseX1StructuredComment )
551  {
552  prepend_string = "G04 #@! ";
553  eol_string = "*\n";
554  }
555  else
556  {
557  prepend_string = "%";
558  eol_string = "*%\n";
559  }
560 
561  // print a Gerber net attribute record.
562  // it is added to the object attributes dictionary
563  // On file, only modified or new attributes are printed.
564  if( aData == nullptr )
565  return false;
566 
567  std::string pad_attribute_string;
568  std::string net_attribute_string;
569  std::string cmp_attribute_string;
570 
572  return false; // idle command: do nothing
573 
575  {
576  // print info associated to a flashed pad (cmpref, pad name, and optionally pin function)
577  // example1: %TO.P,R5,3*%
578  // example2: %TO.P,R5,3,reset*%
579  pad_attribute_string = prepend_string + "TO.P,";
580  pad_attribute_string += FormatStringToGerber( aData->m_Cmpref ) + ",";
581 
582  if( aData->m_Padname.IsEmpty() )
583  {
584  // Happens for "mechanical" or never connected pads
585  pad_attribute_string += FormatStringToGerber( NO_PAD_NAME );
586  }
587  else
588  {
589  pad_attribute_string += aData->m_Padname.GetGerberString();
590 
591  // In Pcbnew, the pin function comes from the schematic.
592  // so it exists only for named pads
593  if( !aData->m_PadPinFunction.IsEmpty() )
594  {
595  pad_attribute_string += ',';
596  pad_attribute_string += aData->m_PadPinFunction.GetGerberString();
597  }
598  }
599 
600  pad_attribute_string += eol_string;
601  }
602 
604  {
605  // print info associated to a net
606  // example: %TO.N,Clk3*%
607  net_attribute_string = prepend_string + "TO.N,";
608 
609  if( aData->m_Netname.IsEmpty() )
610  {
611  if( aData->m_NotInNet )
612  {
613  // Happens for not connectable pads: mechanical pads
614  // and pads with no padname/num
615  // In this case the net name must be left empty
616  }
617  else
618  {
619  // Happens for not connected pads: use a normalized
620  // dummy name
621  net_attribute_string += FormatStringToGerber( NO_NET_NAME );
622  }
623  }
624  else
625  {
626  net_attribute_string += FormatStringToGerber( aData->m_Netname );
627  }
628 
629  net_attribute_string += eol_string;
630  }
631 
634  {
635  // print info associated to a footprint
636  // example: %TO.C,R2*%
637  // Because GBR_NETINFO_PAD option already contains this info, it is not
638  // created here for a GBR_NETINFO_PAD attribute
639  cmp_attribute_string = prepend_string + "TO.C,";
640  cmp_attribute_string += FormatStringToGerber( aData->m_Cmpref ) + eol_string;
641  }
642 
643  // the full list of requested attributes:
644  std::string full_attribute_string = pad_attribute_string + net_attribute_string
645  + cmp_attribute_string;
646  // the short list of requested attributes
647  // (only modified or new attributes are stored here):
648  std::string short_attribute_string;
649 
650  // Attributes have changed: update attribute string, and see if the previous attribute
651  // list (dictionary in Gerber language) must be cleared
652  if( aLastNetAttributes != full_attribute_string )
653  {
654  // first, remove no longer existing attributes.
655  // Because in KiCad the full attribute list is evaluated for each object,
656  // the entire dictionary is cleared
657  // If m_TryKeepPreviousAttributes is true, only the no longer existing attribute
658  // is cleared.
659  // Note: to avoid interaction between clear attributes and set attributes
660  // the clear attribute is inserted first.
661  bool clearDict = false;
662 
663  if( aLastNetAttributes.find( "TO.P," ) != std::string::npos )
664  {
665  if( pad_attribute_string.empty() ) // No more this attribute
666  {
667  if( aData->m_TryKeepPreviousAttributes ) // Clear only this attribute
668  short_attribute_string.insert( 0, prepend_string + "TO.P" + eol_string );
669  else
670  clearDict = true;
671  }
672  else if( aLastNetAttributes.find( pad_attribute_string ) == std::string::npos )
673  {
674  // This attribute has changed
675  short_attribute_string += pad_attribute_string;
676  }
677  }
678  else // New attribute
679  {
680  short_attribute_string += pad_attribute_string;
681  }
682 
683  if( aLastNetAttributes.find( "TO.N," ) != std::string::npos )
684  {
685  if( net_attribute_string.empty() ) // No more this attribute
686  {
687  if( aData->m_TryKeepPreviousAttributes ) // Clear only this attribute
688  short_attribute_string.insert( 0, prepend_string + "TO.N" + eol_string );
689  else
690  clearDict = true;
691  }
692  else if( aLastNetAttributes.find( net_attribute_string ) == std::string::npos )
693  {
694  // This attribute has changed.
695  short_attribute_string += net_attribute_string;
696  }
697  }
698  else // New attribute
699  {
700  short_attribute_string += net_attribute_string;
701  }
702 
703  if( aLastNetAttributes.find( "TO.C," ) != std::string::npos )
704  {
705  if( cmp_attribute_string.empty() ) // No more this attribute
706  {
707  if( aData->m_TryKeepPreviousAttributes ) // Clear only this attribute
708  {
709  // Refinement:
710  // the attribute will be cleared only if there is no pad attribute.
711  // If a pad attribute exists, the component name exists so the old
712  // TO.C value will be updated, therefore no need to clear it before updating
713  if( pad_attribute_string.empty() )
714  short_attribute_string.insert( 0, prepend_string + "TO.C" + eol_string );
715  }
716  else
717  {
718  clearDict = true;
719  }
720  }
721  else if( aLastNetAttributes.find( cmp_attribute_string ) == std::string::npos )
722  {
723  // This attribute has changed.
724  short_attribute_string += cmp_attribute_string;
725  }
726  }
727  else // New attribute
728  {
729  short_attribute_string += cmp_attribute_string;
730  }
731 
732  aClearPreviousAttributes = clearDict;
733 
734  aLastNetAttributes = full_attribute_string;
735 
736  if( clearDict )
737  aPrintedText = full_attribute_string;
738  else
739  aPrintedText = short_attribute_string;
740  }
741 
742  return true;
743 }
std::string FormatStringToGerber(const wxString &aString)
Normalize aString and convert it to a Gerber std::string.
#define NO_NET_NAME
print info associated to a component (TO.C attribute)
wxString m_Cmpref
the component reference parent of the data
GBR_DATA_FIELD m_PadPinFunction
for a pad: the pin function (defined in schematic)
#define NO_PAD_NAME
GBR_DATA_FIELD m_Padname
for a flashed pad: the pad name ((TO.P attribute)
wxString m_Netname
for items associated to a net: the netname
bool m_TryKeepPreviousAttributes
If true, do not clear all attributes when a attribute has changed.
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)...
std::string GetGerberString() const
print info associated to a net (TO.N attribute)
int m_NetAttribType
the type of net info (used to define the gerber string to create)

References FormatStringToGerber(), GBR_NETLIST_METADATA::GBR_NETINFO_CMP, GBR_NETLIST_METADATA::GBR_NETINFO_NET, GBR_NETLIST_METADATA::GBR_NETINFO_PAD, GBR_NETLIST_METADATA::GBR_NETINFO_UNSPECIFIED, GBR_DATA_FIELD::GetGerberString(), GBR_DATA_FIELD::IsEmpty(), GBR_NETLIST_METADATA::m_Cmpref, GBR_NETLIST_METADATA::m_NetAttribType, GBR_NETLIST_METADATA::m_Netname, GBR_NETLIST_METADATA::m_NotInNet, GBR_NETLIST_METADATA::m_Padname, GBR_NETLIST_METADATA::m_PadPinFunction, GBR_NETLIST_METADATA::m_TryKeepPreviousAttributes, NO_NET_NAME, and NO_PAD_NAME.

Referenced by GERBER_PLOTTER::formatNetAttribute().

◆ FormatStringFromGerber()

wxString FormatStringFromGerber ( const wxString &  aString)

Convert a gerber string into a 16 bit Unicode string.

Parameters
aStringthe gerber string to format.
Returns
a 16 bit Unicode string.

Definition at line 369 of file gbr_metadata.cpp.

370 {
371  // make the inverse conversion of FormatStringToGerber()
372  // It converts a "normalized" gerber string containing escape sequences
373  // and convert it to a 16 bits Unicode char
374  // and return a wxString (Unicode 16) from the gerber string
375  // Note the initial gerber string can already contain Unicode chars.
376  wxString txt; // The string converted from Gerber string
377 
378  unsigned count = aString.Length();
379 
380  for( unsigned ii = 0; ii < count; ++ii )
381  {
382  unsigned code = aString[ii];
383 
384  if( code == '\\' && ii < count-5 && aString[ii+1] == 'u' )
385  {
386  // Note the latest Gerber X2 spec (2019 06) uses \uXXXX to encode
387  // the Unicode XXXX hexadecimal value
388  // If 4 chars next to 'u' are hexadecimal chars,
389  // Convert these 4 hexadecimal digits to a 16 bit Unicode
390  // (Gerber allows only 4 hexadecimal digits)
391  // If an error occurs, the escape sequence is not translated,
392  // and used "as this"
393  long value = 0;
394  bool error = false;
395 
396  for( int jj = 0; jj < 4; jj++ )
397  {
398  value <<= 4;
399  code = aString[ii+jj+2];
400 
401  int hexa = char2Hex( code );
402 
403  if( hexa >= 0 )
404  value += hexa;
405  else
406  {
407  error = true;
408  break;
409  }
410  }
411 
412  if( !error )
413  {
414  if( value >= ' ' ) // Is a valid wxChar ?
415  txt.Append( wxChar( value ) );
416 
417  ii += 5;
418  }
419  else
420  {
421  txt.Append( aString[ii] );
422  continue;
423  }
424  }
425  else
426  {
427  txt.Append( aString[ii] );
428  }
429  }
430 
431  return txt;
432 }
int char2Hex(unsigned aCode)

References char2Hex().

Referenced by GERBER_FILE_IMAGE::ExecuteRS274XCommand().

◆ FormatStringToGerber()

std::string FormatStringToGerber ( const wxString &  aString)

Normalize aString and convert it to a Gerber std::string.

Normalization means convert any code > 0x7F and unauthorized code to a hexadecimal 16 bit sequence Unicode. Illegal characters are ',' '*' '' '\'.

Parameters
aStringthe string to convert.
Returns
an ASCII7 coded compliant gerber string.

Definition at line 512 of file gbr_metadata.cpp.

513 {
514  wxString converted;
515 
516  /* format string means convert any code > 0x7E and unauthorized codes to a hexadecimal
517  * 16 bits sequence Unicode
518  * unauthorized codes are ',' '*' '%' '\'
519  * This conversion is not made for quoted strings, because if the string is
520  * quoted, the conversion is expected to be already made, and the returned string must use
521  * UTF8 encoding
522  */
523  if( !aString.IsEmpty() && ( aString[0] != '\"' || aString[aString.Len()-1] != '\"' ) )
524  converted = ConvertNotAllowedCharsInGerber( aString, false, false );
525  else
526  converted = aString;
527 
528  // Convert the char string to std::string. Be careful when converting a wxString to
529  // a std::string: using static_cast<const char*> is mandatory
530  std::string txt = static_cast<const char*>( converted.utf8_str() );
531 
532  return txt;
533 }
wxString ConvertNotAllowedCharsInGerber(const wxString &aString, bool aAllowUtf8Chars, bool aQuoteString)
Normalize aString and convert it to a Gerber compatible wxString.

References ConvertNotAllowedCharsInGerber().

Referenced by FormatNetAttribute().

◆ GbrMakeCreationDateAttributeString()

wxString GbrMakeCreationDateAttributeString ( GBR_NC_STRING_FORMAT  aFormat)

Definition at line 37 of file gbr_metadata.cpp.

38 {
39  // creates the CreationDate attribute:
40  // The attribute value must conform to the full version of the ISO 8601
41  // date and time format, including time and time zone. Note that this is
42  // the date the Gerber file was effectively created,
43  // not the time the project of PCB was started
44  wxDateTime date( wxDateTime::GetTimeNow() );
45 
46  // Date format: see http://www.cplusplus.com/reference/ctime/strftime
47  wxString timezone_offset; // ISO 8601 offset from UTC in timezone
48  timezone_offset = date.Format( "%z" ); // Extract the time zone offset
49 
50  // The time zone offset format is +mm or +hhmm (or -mm or -hhmm)
51  // (mm = number of minutes, hh = number of hours. 1h00mn is returned as +0100)
52  // we want +(or -) hh:mm
53  if( timezone_offset.Len() > 3 ) // format +hhmm or -hhmm found
54  // Add separator between hours and minutes
55  timezone_offset.insert( 3, ":", 1 );
56 
57  wxString msg;
58 
59  switch( aFormat )
60  {
62  msg.Printf( "%%TF.CreationDate,%s%s*%%", date.FormatISOCombined(), timezone_offset );
63  break;
64 
66  msg.Printf( "G04 #@! TF.CreationDate,%s%s*", date.FormatISOCombined(), timezone_offset );
67  break;
68 
70  msg.Printf( "%s%s", date.FormatISOCombined(), timezone_offset );
71  break;
72 
74  msg.Printf( "; #@! TF.CreationDate,%s%s", date.FormatISOCombined(), timezone_offset );
75  break;
76  }
77  return msg;
78 }

References GBR_NC_STRING_FORMAT_GBRJOB, GBR_NC_STRING_FORMAT_NCDRILL, GBR_NC_STRING_FORMAT_X1, and GBR_NC_STRING_FORMAT_X2.

Referenced by AddGerberX2Header(), GERBER_JOBFILE_WRITER::addJSONHeader(), and EXCELLON_WRITER::writeEXCELLONHeader().

◆ GbrMakeProjectGUIDfromString()

wxString GbrMakeProjectGUIDfromString ( const wxString &  aText)

Build a project GUID using format RFC4122 Version 1 or 4 from the project name, because a KiCad project has no specific GUID.

RFC4122 is used mainly for its syntax, because fields have no meaning for Gerber files and therefore the GUID generated has no meaning because it do not use any time and time stamp specific to the project, just a random pattern (random is here a pattern specific to a project).

See en.wikipedia.org/wiki/Universally_unique_identifier

Definition at line 81 of file gbr_metadata.cpp.

82 {
83  /* Gerber GUID format should be RFC4122 Version 1 or 4.
84  * See en.wikipedia.org/wiki/Universally_unique_identifier
85  * The format is:
86  * xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
87  * with
88  * x = hexDigit lower/upper case
89  * and
90  * M = '1' or '4' (UUID version: 1 (basic) or 4 (random)) (we use 4: UUID random)
91  * and
92  * N = '8' or '9' or 'A|a' or 'B|b' : UUID variant 1: 2 MSB bits have meaning) (we use N = 9)
93  * N = 1000 or 1001 or 1010 or 1011 : 10xx means Variant 1 (Variant2: 110x and 111x are
94  * reserved)
95  */
96 
97  wxString guid;
98 
99  // Build a 32 digits GUID from the board name:
100  // guid has 32 digits, so add chars in name to be sure we can build a 32 digits guid
101  // (i.e. from a 16 char string name)
102  // In fact only 30 digits are used, and 2 UID id
103  wxString bname = aText;
104  int cnt = 16 - bname.Len();
105 
106  if( cnt > 0 )
107  bname.Append( 'X', cnt );
108 
109  int chr_idx = 0;
110 
111  // Output the 8 first hex digits:
112  for( unsigned ii = 0; ii < 4; ii++ )
113  {
114  int cc = int( bname[chr_idx++] ) & 0xFF;
115  guid << wxString::Format( "%2.2x", cc );
116  }
117 
118  // Output the 4 next hex digits:
119  guid << '-';
120 
121  for( unsigned ii = 0; ii < 2; ii++ )
122  {
123  int cc = int( bname[chr_idx++] ) & 0xFF;
124  guid << wxString::Format( "%2.2x", cc );
125  }
126 
127  // Output the 4 next hex digits (UUID version and 3 digits):
128  guid << "-4"; // first digit: UUID version 4 (M = 4)
129  {
130  int cc = int( bname[chr_idx++] ) << 4 & 0xFF0;
131  cc += int( bname[chr_idx] ) >> 4 & 0x0F;
132  guid << wxString::Format( "%3.3x", cc );
133  }
134 
135  // Output the 4 next hex digits (UUID variant and 3 digits):
136  guid << "-9"; // first digit: UUID variant 1 (N = 9)
137  {
138  int cc = (int( bname[chr_idx++] ) & 0x0F) << 8;
139  cc += int( bname[chr_idx++] ) & 0xFF;
140  guid << wxString::Format( "%3.3x", cc );
141  }
142 
143  // Output the 12 last hex digits:
144  guid << '-';
145 
146  for( unsigned ii = 0; ii < 6; ii++ )
147  {
148  int cc = int( bname[chr_idx++] ) & 0xFF;
149  guid << wxString::Format( "%2.2x", cc );
150  }
151 
152  return guid;
153 }
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

References Format().

Referenced by AddGerberX2Header(), and GERBER_JOBFILE_WRITER::addJSONGeneralSpecs().