2016-09-19 11:01:36 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2019-02-25 13:09:18 +00:00
|
|
|
* Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
|
|
|
* Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
|
2016-09-19 11:01:36 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file gbr_metadata.cpp
|
|
|
|
* @brief helper functions to handle the gerber metadata in files,
|
|
|
|
* related to the netlist info and aperture attribute.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <fctsys.h>
|
2018-05-17 05:49:38 +00:00
|
|
|
#include <gbr_metadata.h>
|
2019-10-24 16:50:51 +00:00
|
|
|
#include <utf8.h>
|
2018-11-06 07:16:07 +00:00
|
|
|
|
2018-11-21 14:33:17 +00:00
|
|
|
wxString GbrMakeCreationDateAttributeString( GBR_NC_STRING_FORMAT aFormat )
|
|
|
|
{
|
|
|
|
// creates the CreationDate attribute:
|
|
|
|
// The attribute value must conform to the full version of the ISO 8601
|
|
|
|
// date and time format, including time and time zone. Note that this is
|
|
|
|
// the date the Gerber file was effectively created,
|
|
|
|
// not the time the project of PCB was started
|
|
|
|
wxDateTime date( wxDateTime::GetTimeNow() );
|
|
|
|
// Date format: see http://www.cplusplus.com/reference/ctime/strftime
|
|
|
|
wxString timezone_offset; // ISO 8601 offset from UTC in timezone
|
|
|
|
timezone_offset = date.Format( "%z" ); // Extract the time zone offset
|
|
|
|
// The time zone offset format is +mm or +hhmm (or -mm or -hhmm)
|
|
|
|
// (mm = number of minutes, hh = number of hours. 1h00mn is returned as +0100)
|
|
|
|
// we want +(or -) hh:mm
|
|
|
|
if( timezone_offset.Len() > 3 ) // format +hhmm or -hhmm found
|
|
|
|
// Add separator between hours and minutes
|
|
|
|
timezone_offset.insert( 3, ":", 1 );
|
|
|
|
|
|
|
|
wxString msg;
|
|
|
|
|
|
|
|
switch( aFormat )
|
|
|
|
{
|
|
|
|
case GBR_NC_STRING_FORMAT_X2:
|
|
|
|
msg.Printf( "%%TF.CreationDate,%s%s*%%", date.FormatISOCombined(), timezone_offset );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_NC_STRING_FORMAT_X1:
|
2019-02-25 13:09:18 +00:00
|
|
|
msg.Printf( "G04 #@! TF.CreationDate,%s%s*", date.FormatISOCombined(), timezone_offset );
|
2018-11-21 14:33:17 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_NC_STRING_FORMAT_GBRJOB:
|
|
|
|
msg.Printf( "\"CreationDate\": \"%s%s\"", date.FormatISOCombined(), timezone_offset );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_NC_STRING_FORMAT_NCDRILL:
|
2019-02-27 09:41:55 +00:00
|
|
|
msg.Printf( "; #@! TF.CreationDate,%s%s", date.FormatISOCombined(), timezone_offset );
|
2018-11-21 14:33:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2018-11-06 07:16:07 +00:00
|
|
|
|
|
|
|
wxString GbrMakeProjectGUIDfromString( wxString& aText )
|
|
|
|
{
|
|
|
|
/* Gerber GUID format should be RFC4122 Version 1 or 4.
|
|
|
|
* See en.wikipedia.org/wiki/Universally_unique_identifier
|
|
|
|
* The format is:
|
|
|
|
* xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
|
|
|
|
* with
|
|
|
|
* x = hexDigit lower/upper case
|
|
|
|
* and
|
|
|
|
* M = '1' or '4' (UUID version: 1 (basic) or 4 (random)) (we use 4: UUID random)
|
|
|
|
* and
|
|
|
|
* N = '8' or '9' or 'A|a' or 'B|b' : UUID variant 1: 2 MSB bits have meaning) (we use N = 9)
|
|
|
|
* N = 1000 or 1001 or 1010 or 1011 : 10xx means Variant 1 (Variant2: 110x and 111x are reserved)
|
|
|
|
*/
|
|
|
|
|
|
|
|
wxString guid;
|
|
|
|
|
|
|
|
// Build a 32 digits GUID from the board name:
|
|
|
|
// guid has 32 digits, so add chars in name to be sure we can build a 32 digits guid
|
|
|
|
// (i.e. from a 16 char string name)
|
|
|
|
// In fact only 30 digits are used, and 2 UID id
|
|
|
|
wxString bname = aText;
|
|
|
|
int cnt = 16 - bname.Len();
|
|
|
|
|
|
|
|
if( cnt > 0 )
|
|
|
|
bname.Append( 'X', cnt );
|
|
|
|
|
|
|
|
int chr_idx = 0;
|
|
|
|
|
|
|
|
// Output the 8 first hex digits:
|
|
|
|
for( unsigned ii = 0; ii < 4; ii++ )
|
|
|
|
{
|
|
|
|
int cc = int( bname[chr_idx++] ) & 0xFF;
|
|
|
|
guid << wxString::Format( "%2.2x", cc );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output the 4 next hex digits:
|
|
|
|
guid << '-';
|
|
|
|
|
|
|
|
for( unsigned ii = 0; ii < 2; ii++ )
|
|
|
|
{
|
|
|
|
int cc = int( bname[chr_idx++] ) & 0xFF;
|
|
|
|
guid << wxString::Format( "%2.2x", cc );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output the 4 next hex digits (UUID version and 3 digits):
|
|
|
|
guid << "-4"; // first digit: UUID version 4 (M = 4)
|
|
|
|
{
|
|
|
|
int cc = int( bname[chr_idx++] ) << 4 & 0xFF0;
|
|
|
|
cc += int( bname[chr_idx] ) >> 4 & 0x0F;
|
|
|
|
guid << wxString::Format( "%3.3x", cc );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output the 4 next hex digits (UUID variant and 3 digits):
|
|
|
|
guid << "-9"; // first digit: UUID variant 1 (N = 9)
|
|
|
|
{
|
|
|
|
int cc = (int( bname[chr_idx++] ) & 0x0F) << 8;
|
|
|
|
cc += int( bname[chr_idx++] ) & 0xFF;
|
|
|
|
guid << wxString::Format( "%3.3x", cc );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output the 12 last hex digits:
|
|
|
|
guid << '-';
|
|
|
|
|
|
|
|
for( unsigned ii = 0; ii < 6; ii++ )
|
|
|
|
{
|
|
|
|
int cc = int( bname[chr_idx++] ) & 0xFF;
|
|
|
|
guid << wxString::Format( "%2.2x", cc );
|
|
|
|
}
|
|
|
|
|
|
|
|
return guid;
|
|
|
|
}
|
2016-09-19 11:01:36 +00:00
|
|
|
|
|
|
|
|
2018-05-17 06:54:25 +00:00
|
|
|
std::string GBR_APERTURE_METADATA::FormatAttribute( GBR_APERTURE_ATTRIB aAttribute,
|
|
|
|
bool aUseX1StructuredComment )
|
2016-09-19 11:01:36 +00:00
|
|
|
{
|
2019-06-20 16:23:54 +00:00
|
|
|
std::string attribute_string; // the specific aperture attribute (TA.xxx)
|
|
|
|
std::string comment_string; // a optional G04 comment line to write before the TA. line
|
2016-09-19 11:01:36 +00:00
|
|
|
|
|
|
|
// generate a string to print a Gerber Aperture attribute
|
|
|
|
switch( aAttribute )
|
|
|
|
{
|
|
|
|
case GBR_APERTURE_ATTRIB_END: // Dummy value (aAttribute must be < GBR_APERTURE_ATTRIB_END)
|
|
|
|
case GBR_APERTURE_ATTRIB_NONE: // idle command: do nothing
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_ETCHEDCMP: // print info associated to an item
|
|
|
|
// which connects 2 different nets
|
|
|
|
// (Net tees, microwave component)
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,EtchedComponent";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_CONDUCTOR: // print info associated to a track
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,Conductor";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
2019-10-09 13:46:35 +00:00
|
|
|
case GBR_APERTURE_ATTRIB_EDGECUT: // print info associated to a board outline
|
|
|
|
attribute_string = "TA.AperFunction,Profile";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_VIAPAD: // print info associated to a flashed via
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,ViaPad";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
2017-05-03 18:04:31 +00:00
|
|
|
case GBR_APERTURE_ATTRIB_NONCONDUCTOR: // print info associated to a item on a copper layer
|
|
|
|
// which is not a track (for instance a text)
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,NonConductor";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_COMPONENTPAD: // print info associated to a flashed
|
|
|
|
// through hole component on outer layer
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,ComponentPad";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_SMDPAD_SMDEF: // print info associated to a flashed for SMD pad.
|
|
|
|
// with solder mask defined from the copper shape
|
|
|
|
// Excluded BGA pads which have their own type
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,SMDPad,SMDef";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_SMDPAD_CUDEF: // print info associated to a flashed SMD pad with
|
|
|
|
// a solder mask defined by the solder mask
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,SMDPad,CuDef";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_BGAPAD_SMDEF: // print info associated to flashed BGA pads with
|
|
|
|
// a solder mask defined by the copper shape
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,BGAPad,SMDef";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_BGAPAD_CUDEF: // print info associated to a flashed BGA pad with
|
|
|
|
// a solder mask defined by the solder mask
|
2018-05-29 10:30:52 +00:00
|
|
|
attribute_string = "TA.AperFunction,BGAPad,CuDef";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_CONNECTORPAD: // print info associated to a flashed edge connector pad (outer layers)
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,ConnectorPad";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_WASHERPAD: // print info associated to flashed mechanical pads (NPTH)
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,WasherPad";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-28 10:53:30 +00:00
|
|
|
case GBR_APERTURE_ATTRIB_HEATSINKPAD: // print info associated to a flashed heat sink pad
|
|
|
|
// (typically for SMDs)
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,HeatsinkPad";
|
2016-09-19 11:01:36 +00:00
|
|
|
break;
|
2017-04-28 10:53:30 +00:00
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_VIADRILL: // print info associated to a via hole in drill files
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,ViaDrill";
|
2017-04-28 10:53:30 +00:00
|
|
|
break;
|
|
|
|
|
2019-09-18 09:48:49 +00:00
|
|
|
case GBR_APERTURE_ATTRIB_CMP_DRILL: // print info associated to a component
|
|
|
|
// round pad hole in drill files
|
2018-05-17 06:54:25 +00:00
|
|
|
attribute_string = "TA.AperFunction,ComponentDrill";
|
2017-04-28 10:53:30 +00:00
|
|
|
break;
|
|
|
|
|
2019-06-20 16:23:54 +00:00
|
|
|
// print info associated to a component oblong pad hole in drill files
|
|
|
|
// Same as a round pad hole, but is a specific aperture in drill file and
|
|
|
|
// a G04 comment is added to the aperture function
|
2019-09-18 09:48:49 +00:00
|
|
|
case GBR_APERTURE_ATTRIB_CMP_OBLONG_DRILL:
|
2019-06-20 16:23:54 +00:00
|
|
|
comment_string = "aperture for slot hole";
|
|
|
|
attribute_string = "TA.AperFunction,ComponentDrill";
|
2019-09-18 09:48:49 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_CMP_POSITION: // print info associated to a component
|
|
|
|
// flashed shape at the component position
|
|
|
|
// in placement files
|
|
|
|
attribute_string = "TA.AperFunction,ComponentMain";
|
|
|
|
break;
|
|
|
|
|
2019-09-15 15:08:57 +00:00
|
|
|
case GBR_APERTURE_ATTRIB_PAD1_POSITION: // print info associated to a component
|
|
|
|
// flashed shape at pad 1 position
|
|
|
|
// (pad 1 is also pad A1 or pad AA1)
|
|
|
|
// in placement files
|
|
|
|
attribute_string = "TA.AperFunction,ComponentPin";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_PADOTHER_POSITION: // print info associated to a component
|
|
|
|
// flashed shape at pads position (all but pad 1)
|
2019-09-18 09:48:49 +00:00
|
|
|
// in placement files
|
2019-09-15 15:08:57 +00:00
|
|
|
// Currently: (could be changed later) same as
|
|
|
|
// GBR_APERTURE_ATTRIB_PADOTHER_POSITION
|
|
|
|
attribute_string = "TA.AperFunction,ComponentPin";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_CMP_BODY: // print info associated to a component
|
|
|
|
// print the component physical body
|
|
|
|
// polygon in placement files
|
|
|
|
attribute_string = "TA.AperFunction,ComponentOutline,Body";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_CMP_LEAD2LEAD: // print info associated to a component
|
|
|
|
// print the component physical lead to lead
|
|
|
|
// polygon in placement files
|
|
|
|
attribute_string = "TA.AperFunction,ComponentOutline,Lead2Lead";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_CMP_FOOTPRINT: // print info associated to a component
|
|
|
|
// print the component footprint bounding box
|
|
|
|
// polygon in placement files
|
|
|
|
attribute_string = "TA.AperFunction,ComponentOutline,Footprint";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GBR_APERTURE_ATTRIB_CMP_COURTYARD: // print info associated to a component
|
|
|
|
// print the component courtyard
|
|
|
|
// polygon in placement files
|
2019-09-18 09:48:49 +00:00
|
|
|
attribute_string = "TA.AperFunction,ComponentOutline,Courtyard";
|
|
|
|
break;
|
2019-06-20 16:23:54 +00:00
|
|
|
|
|
|
|
break;
|
2016-09-19 11:01:36 +00:00
|
|
|
}
|
|
|
|
|
2018-05-17 06:54:25 +00:00
|
|
|
std::string full_attribute_string;
|
|
|
|
wxString eol_string;
|
|
|
|
|
|
|
|
if( !attribute_string.empty() )
|
|
|
|
{
|
2019-06-20 16:23:54 +00:00
|
|
|
if( !comment_string.empty() )
|
|
|
|
{
|
|
|
|
full_attribute_string = "G04 " + comment_string + "*\n";
|
|
|
|
}
|
|
|
|
|
2018-05-17 06:54:25 +00:00
|
|
|
if( aUseX1StructuredComment )
|
|
|
|
{
|
2019-06-20 16:23:54 +00:00
|
|
|
full_attribute_string += "G04 #@! ";
|
2018-05-17 06:54:25 +00:00
|
|
|
eol_string = "*\n";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-06-20 16:23:54 +00:00
|
|
|
full_attribute_string += "%";
|
2018-05-17 06:54:25 +00:00
|
|
|
eol_string = "*%\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
full_attribute_string += attribute_string + eol_string;
|
|
|
|
|
|
|
|
return full_attribute_string;
|
2016-09-19 11:01:36 +00:00
|
|
|
}
|
|
|
|
|
2019-10-24 16:50:51 +00:00
|
|
|
|
|
|
|
// Helper function to convert a ascii hex char to its integer value
|
|
|
|
// If the char is not a hexa char, return -1
|
|
|
|
int char2Hex( unsigned aCode )
|
|
|
|
{
|
|
|
|
if( aCode >= '0' && aCode <= '9' )
|
|
|
|
return aCode - '0';
|
|
|
|
|
|
|
|
if( aCode >= 'A' && aCode <= 'F' )
|
|
|
|
return aCode - 'A' + 10;
|
|
|
|
|
|
|
|
if( aCode >= 'a' && aCode <= 'f' )
|
|
|
|
return aCode - 'a' + 10;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-19 16:28:11 +00:00
|
|
|
wxString FormatStringFromGerber( const wxString& aString )
|
|
|
|
{
|
2019-10-24 16:50:51 +00:00
|
|
|
// make the inverse conversion of FormatStringToGerber()
|
2019-10-31 20:03:17 +00:00
|
|
|
// It converts a "normalized" gerber string containing escape sequences
|
|
|
|
// and convert it to a 16 bits unicode char
|
2017-08-19 16:28:11 +00:00
|
|
|
// and return a wxString (unicode 16) from the gerber string
|
2019-10-31 20:03:17 +00:00
|
|
|
// Note the initial gerber string can already contain unicode chars.
|
2019-10-24 16:50:51 +00:00
|
|
|
wxString txt; // The string converted from Gerber string
|
2017-08-19 16:28:11 +00:00
|
|
|
|
2019-10-24 16:50:51 +00:00
|
|
|
unsigned count = aString.Length();
|
|
|
|
|
|
|
|
for( unsigned ii = 0; ii < count; ++ii )
|
2017-08-19 16:28:11 +00:00
|
|
|
{
|
|
|
|
unsigned code = aString[ii];
|
|
|
|
|
2019-12-21 10:15:57 +00:00
|
|
|
if( code == '\\' && ii < count-5 && aString[ii+1] == 'u' )
|
2017-08-19 16:28:11 +00:00
|
|
|
{
|
2019-12-21 10:15:57 +00:00
|
|
|
// Note the latest Gerber X2 spec (2019 06) uses \uXXXX to encode
|
|
|
|
// the unicode XXXX hexadecimal value
|
|
|
|
// If 4 chars next to 'u' are hexadecimal chars,
|
|
|
|
// Convert these 4 hexadecimal digits to a 16 bit unicode
|
2017-08-19 16:28:11 +00:00
|
|
|
// (Gerber allows only 4 hexadecimal digits)
|
2019-12-21 10:15:57 +00:00
|
|
|
// If an error occurs, the escape sequence is not translated,
|
|
|
|
// and used "as this"
|
2017-08-19 16:28:11 +00:00
|
|
|
long value = 0;
|
2019-12-21 10:15:57 +00:00
|
|
|
bool error = false;
|
2017-08-19 16:28:11 +00:00
|
|
|
|
|
|
|
for( int jj = 0; jj < 4; jj++ )
|
|
|
|
{
|
|
|
|
value <<= 4;
|
2019-12-21 10:15:57 +00:00
|
|
|
code = aString[ii+jj+2];
|
|
|
|
|
2019-10-24 16:50:51 +00:00
|
|
|
int hexa = char2Hex( code );
|
2019-12-21 10:15:57 +00:00
|
|
|
|
|
|
|
if( hexa >= 0 )
|
|
|
|
value += hexa;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
error = true;
|
|
|
|
break;
|
|
|
|
}
|
2017-08-19 16:28:11 +00:00
|
|
|
}
|
|
|
|
|
2019-12-21 10:15:57 +00:00
|
|
|
if( !error )
|
|
|
|
{
|
|
|
|
if( value >= ' ' ) // Is a valid wxChar ?
|
|
|
|
txt.Append( wxChar( value ) );
|
|
|
|
|
|
|
|
ii += 5;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
txt.Append( aString[ii] );
|
|
|
|
continue;
|
|
|
|
}
|
2017-08-19 16:28:11 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
txt.Append( aString[ii] );
|
|
|
|
}
|
|
|
|
|
|
|
|
return txt;
|
|
|
|
}
|
|
|
|
|
2016-09-19 11:01:36 +00:00
|
|
|
|
2019-10-24 16:50:51 +00:00
|
|
|
wxString ConvertNotAllowedCharsInGerber( const wxString& aString, bool aAllowUtf8Chars, bool aQuoteString )
|
2016-09-19 11:01:36 +00:00
|
|
|
{
|
2019-10-24 16:50:51 +00:00
|
|
|
/* format string means convert any code > 0x7E and unautorized codes to a hexadecimal
|
2016-09-19 11:01:36 +00:00
|
|
|
* 16 bits sequence unicode
|
2019-12-21 10:15:57 +00:00
|
|
|
* However if aAllowUtf8Chars is true only unautorized codes will be escaped, because some
|
|
|
|
* Gerber files accept UTF8 chars.
|
2019-12-28 12:26:46 +00:00
|
|
|
* unautorized codes are ',' '*' '%' '\' '"' and are used as separators in Gerber files
|
2016-09-19 11:01:36 +00:00
|
|
|
*/
|
2019-10-24 16:50:51 +00:00
|
|
|
wxString txt;
|
2016-09-19 11:01:36 +00:00
|
|
|
|
2019-10-24 16:50:51 +00:00
|
|
|
if( aQuoteString )
|
|
|
|
txt << "\"";
|
2016-09-19 11:01:36 +00:00
|
|
|
|
|
|
|
for( unsigned ii = 0; ii < aString.Length(); ++ii )
|
|
|
|
{
|
2019-10-24 16:50:51 +00:00
|
|
|
wxChar code = aString[ii];
|
2016-09-19 11:01:36 +00:00
|
|
|
bool convert = false;
|
|
|
|
|
|
|
|
switch( code )
|
|
|
|
{
|
|
|
|
case '\\':
|
|
|
|
case '%':
|
|
|
|
case '*':
|
|
|
|
case ',':
|
|
|
|
convert = true;
|
|
|
|
break;
|
|
|
|
|
2019-12-28 12:26:46 +00:00
|
|
|
case '"':
|
|
|
|
if( aQuoteString )
|
|
|
|
convert = true;
|
|
|
|
break;
|
|
|
|
|
2016-09-19 11:01:36 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-10-24 16:50:51 +00:00
|
|
|
if( !aAllowUtf8Chars && code > 0x7F )
|
|
|
|
convert = true;
|
|
|
|
|
|
|
|
if( convert )
|
2016-09-19 11:01:36 +00:00
|
|
|
{
|
|
|
|
// Convert code to 4 hexadecimal digit
|
2019-12-21 10:15:57 +00:00
|
|
|
// (Gerber allows only 4 hexadecimal digit) in escape seq:
|
|
|
|
// "\uXXXX", XXXX is the unicode 16 bits hexa value
|
2016-09-19 11:01:36 +00:00
|
|
|
char hexa[32];
|
2019-12-21 10:15:57 +00:00
|
|
|
sprintf( hexa,"\\u%4.4X", code & 0xFFFF);
|
2016-09-19 11:01:36 +00:00
|
|
|
txt += hexa;
|
|
|
|
}
|
|
|
|
else
|
2019-10-24 16:50:51 +00:00
|
|
|
txt += code;
|
2016-09-19 11:01:36 +00:00
|
|
|
}
|
|
|
|
|
2019-10-24 16:50:51 +00:00
|
|
|
if( aQuoteString )
|
|
|
|
txt << "\"";
|
|
|
|
|
|
|
|
return txt;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-17 09:11:29 +00:00
|
|
|
std::string GBR_DATA_FIELD::GetGerberString()
|
|
|
|
{
|
|
|
|
wxString converted;
|
|
|
|
|
|
|
|
if( !m_field.IsEmpty() )
|
|
|
|
converted = ConvertNotAllowedCharsInGerber( m_field, m_useUTF8, m_escapeString );
|
|
|
|
|
|
|
|
// Convert the char string to std::string. Be carefull when converting awxString to
|
|
|
|
// a std::string: using static_cast<const char*> is mandatory
|
|
|
|
std::string txt = static_cast<const char*>( converted.utf8_str() );
|
|
|
|
|
|
|
|
return txt;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-24 16:50:51 +00:00
|
|
|
std::string FormatStringToGerber( const wxString& aString )
|
|
|
|
{
|
|
|
|
wxString converted;
|
|
|
|
/* format string means convert any code > 0x7E and unautorized codes to a hexadecimal
|
|
|
|
* 16 bits sequence unicode
|
|
|
|
* unautorized codes are ',' '*' '%' '\'
|
|
|
|
* This conversion is not made for quoted strings, because if the string is
|
|
|
|
* quoted, the conversion is expected to be already made, and the returned string must use
|
|
|
|
* UTF8 encoding
|
|
|
|
*/
|
|
|
|
if( aString[0] != '\"' || aString[aString.Len()-1] != '\"' )
|
|
|
|
converted = ConvertNotAllowedCharsInGerber( aString, false, false );
|
|
|
|
else
|
|
|
|
converted = aString;
|
|
|
|
|
|
|
|
// Convert the char string to std::string. Be carefull when converting awxString to
|
|
|
|
// a std::string: using static_cast<const char*> is mandatory
|
|
|
|
std::string txt = static_cast<const char*>( converted.utf8_str() );
|
|
|
|
|
2016-09-19 11:01:36 +00:00
|
|
|
return txt;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Netname and Pan num fields cannot be empty in Gerber files
|
|
|
|
// Normalized names must be used, if any
|
|
|
|
#define NO_NET_NAME wxT( "N/C" ) // net name of not connected pads (one pad net) (normalized)
|
|
|
|
#define NO_PAD_NAME wxT( "" ) // pad name of pads without pad name/number (not normalized)
|
|
|
|
|
|
|
|
bool FormatNetAttribute( std::string& aPrintedText, std::string& aLastNetAttributes,
|
2018-05-17 06:54:25 +00:00
|
|
|
GBR_NETLIST_METADATA* aData, bool& aClearPreviousAttributes,
|
|
|
|
bool aUseX1StructuredComment )
|
2016-09-19 11:01:36 +00:00
|
|
|
{
|
|
|
|
aClearPreviousAttributes = false;
|
2018-05-17 06:54:25 +00:00
|
|
|
wxString prepend_string;
|
|
|
|
wxString eol_string;
|
|
|
|
|
|
|
|
if( aUseX1StructuredComment )
|
|
|
|
{
|
|
|
|
prepend_string = "G04 #@! ";
|
|
|
|
eol_string = "*\n";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prepend_string = "%";
|
|
|
|
eol_string = "*%\n";
|
|
|
|
}
|
2016-09-19 11:01:36 +00:00
|
|
|
|
|
|
|
// print a Gerber net attribute record.
|
2019-08-20 17:22:30 +00:00
|
|
|
// it is added to the object attributes dictionary
|
2016-09-19 11:01:36 +00:00
|
|
|
// On file, only modified or new attributes are printed.
|
|
|
|
if( aData == NULL )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::string pad_attribute_string;
|
|
|
|
std::string net_attribute_string;
|
|
|
|
std::string cmp_attribute_string;
|
|
|
|
|
|
|
|
if( aData->m_NetAttribType == GBR_NETLIST_METADATA::GBR_NETINFO_UNSPECIFIED )
|
|
|
|
return false; // idle command: do nothing
|
|
|
|
|
|
|
|
if( ( aData->m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_PAD ) )
|
|
|
|
{
|
2019-11-17 09:11:29 +00:00
|
|
|
// print info associated to a flashed pad (cmpref, pad name, and optionally pin function)
|
|
|
|
// example1: %TO.P,R5,3*%
|
|
|
|
// example2: %TO.P,R5,3,reset*%
|
2018-05-17 06:54:25 +00:00
|
|
|
pad_attribute_string = prepend_string + "TO.P,";
|
2019-10-24 16:50:51 +00:00
|
|
|
pad_attribute_string += FormatStringToGerber( aData->m_Cmpref ) + ",";
|
2016-09-19 11:01:36 +00:00
|
|
|
|
|
|
|
if( aData->m_Padname.IsEmpty() )
|
|
|
|
// Happens for "mechanical" or never connected pads
|
2019-10-24 16:50:51 +00:00
|
|
|
pad_attribute_string += FormatStringToGerber( NO_PAD_NAME );
|
2016-09-19 11:01:36 +00:00
|
|
|
else
|
2019-11-17 09:11:29 +00:00
|
|
|
{
|
|
|
|
pad_attribute_string += aData->m_Padname.GetGerberString();
|
|
|
|
|
|
|
|
// In Pcbnew, the pin function comes from the schematic.
|
|
|
|
// so it exists only for named pads
|
|
|
|
if( !aData->m_PadPinFunction.IsEmpty() )
|
|
|
|
{
|
|
|
|
pad_attribute_string += ',';
|
|
|
|
pad_attribute_string += aData->m_PadPinFunction.GetGerberString();
|
|
|
|
}
|
|
|
|
}
|
2016-09-19 11:01:36 +00:00
|
|
|
|
2018-05-17 06:54:25 +00:00
|
|
|
pad_attribute_string += eol_string;
|
2016-09-19 11:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if( ( aData->m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_NET ) )
|
|
|
|
{
|
|
|
|
// print info associated to a net
|
|
|
|
// example: %TO.N,Clk3*%
|
2018-05-17 06:54:25 +00:00
|
|
|
net_attribute_string = prepend_string + "TO.N,";
|
2016-09-19 11:01:36 +00:00
|
|
|
|
|
|
|
if( aData->m_Netname.IsEmpty() )
|
|
|
|
{
|
|
|
|
if( aData->m_NotInNet )
|
|
|
|
{
|
|
|
|
// Happens for not connectable pads: mechanical pads
|
|
|
|
// and pads with no padname/num
|
|
|
|
// In this case the net name must be left empty
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Happens for not connected pads: use a normalized
|
|
|
|
// dummy name
|
2019-10-24 16:50:51 +00:00
|
|
|
net_attribute_string += FormatStringToGerber( NO_NET_NAME );
|
2016-09-19 11:01:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2019-10-24 16:50:51 +00:00
|
|
|
net_attribute_string += FormatStringToGerber( aData->m_Netname );
|
2016-09-19 11:01:36 +00:00
|
|
|
|
2018-05-17 06:54:25 +00:00
|
|
|
net_attribute_string += eol_string;
|
2016-09-19 11:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if( ( aData->m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_CMP ) &&
|
|
|
|
!( aData->m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_PAD ) )
|
|
|
|
{
|
|
|
|
// print info associated to a footprint
|
|
|
|
// example: %TO.C,R2*%
|
|
|
|
// Because GBR_NETINFO_PAD option already contains this info, it is not
|
|
|
|
// created here for a GBR_NETINFO_PAD attribute
|
2018-05-17 06:54:25 +00:00
|
|
|
cmp_attribute_string = prepend_string + "TO.C,";
|
2019-10-24 16:50:51 +00:00
|
|
|
cmp_attribute_string += FormatStringToGerber( aData->m_Cmpref ) + eol_string;
|
2016-09-19 11:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// the full list of requested attributes:
|
|
|
|
std::string full_attribute_string = pad_attribute_string + net_attribute_string
|
|
|
|
+ cmp_attribute_string;
|
|
|
|
// the short list of requested attributes
|
|
|
|
// (only modified or new attributes are stored here):
|
|
|
|
std::string short_attribute_string;
|
|
|
|
|
2019-10-17 10:41:42 +00:00
|
|
|
// Attributes have changed: update attribute string, and see if the previous attribute
|
|
|
|
// list (dictionary in Gerber language) must be cleared
|
2016-09-19 11:01:36 +00:00
|
|
|
if( aLastNetAttributes != full_attribute_string )
|
|
|
|
{
|
2019-10-17 10:41:42 +00:00
|
|
|
// first, remove no longer existing attributes.
|
2016-09-19 11:01:36 +00:00
|
|
|
// Because in Kicad the full attribute list is evaluated for each object,
|
2019-08-20 17:22:30 +00:00
|
|
|
// the entire dictionary is cleared
|
2019-10-17 10:41:42 +00:00
|
|
|
// If m_TryKeepPreviousAttributes is true, only the no longer existing attribute
|
|
|
|
// is cleared.
|
|
|
|
// Note: to avoid interaction beteween clear attributes and set attributes
|
|
|
|
// the clear attribute is inserted first.
|
2016-09-19 11:01:36 +00:00
|
|
|
bool clearDict = false;
|
|
|
|
|
2018-05-17 06:54:25 +00:00
|
|
|
if( aLastNetAttributes.find( "TO.P," ) != std::string::npos )
|
2016-09-19 11:01:36 +00:00
|
|
|
{
|
|
|
|
if( pad_attribute_string.empty() ) // No more this attribute
|
2019-10-17 10:41:42 +00:00
|
|
|
{
|
|
|
|
if( aData->m_TryKeepPreviousAttributes ) // Clear only this attribute
|
|
|
|
short_attribute_string.insert( 0, prepend_string + "TO.P" + eol_string );
|
|
|
|
else
|
|
|
|
clearDict = true;
|
|
|
|
}
|
2016-09-19 11:01:36 +00:00
|
|
|
else if( aLastNetAttributes.find( pad_attribute_string )
|
|
|
|
== std::string::npos ) // This attribute has changed
|
|
|
|
short_attribute_string += pad_attribute_string;
|
|
|
|
}
|
|
|
|
else // New attribute
|
|
|
|
short_attribute_string += pad_attribute_string;
|
|
|
|
|
2018-05-17 06:54:25 +00:00
|
|
|
if( aLastNetAttributes.find( "TO.N," ) != std::string::npos )
|
2016-09-19 11:01:36 +00:00
|
|
|
{
|
|
|
|
if( net_attribute_string.empty() ) // No more this attribute
|
2019-10-17 10:41:42 +00:00
|
|
|
{
|
|
|
|
if( aData->m_TryKeepPreviousAttributes ) // Clear only this attribute
|
|
|
|
short_attribute_string.insert( 0, prepend_string + "TO.N" + eol_string );
|
|
|
|
else
|
|
|
|
clearDict = true;
|
|
|
|
}
|
2016-09-19 11:01:36 +00:00
|
|
|
else if( aLastNetAttributes.find( net_attribute_string )
|
|
|
|
== std::string::npos ) // This attribute has changed
|
|
|
|
short_attribute_string += net_attribute_string;
|
|
|
|
}
|
|
|
|
else // New attribute
|
|
|
|
short_attribute_string += net_attribute_string;
|
|
|
|
|
2018-05-17 06:54:25 +00:00
|
|
|
if( aLastNetAttributes.find( "TO.C," ) != std::string::npos )
|
2016-09-19 11:01:36 +00:00
|
|
|
{
|
|
|
|
if( cmp_attribute_string.empty() ) // No more this attribute
|
2019-10-17 10:41:42 +00:00
|
|
|
{
|
|
|
|
if( aData->m_TryKeepPreviousAttributes ) // Clear only this attribute
|
|
|
|
{
|
|
|
|
// Refinement:
|
|
|
|
// the attribute will be cleared only if there is no pad attribute.
|
|
|
|
// If a pad attribute exists, the component name exists so the old
|
|
|
|
// TO.C value will be updated, therefore no need to clear it before updating
|
|
|
|
if( pad_attribute_string.empty() )
|
|
|
|
short_attribute_string.insert( 0, prepend_string + "TO.C" + eol_string );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
clearDict = true;
|
|
|
|
}
|
2016-09-19 11:01:36 +00:00
|
|
|
else if( aLastNetAttributes.find( cmp_attribute_string )
|
|
|
|
== std::string::npos ) // This attribute has changed
|
|
|
|
short_attribute_string += cmp_attribute_string;
|
|
|
|
}
|
|
|
|
else // New attribute
|
|
|
|
short_attribute_string += cmp_attribute_string;
|
|
|
|
|
|
|
|
aClearPreviousAttributes = clearDict;
|
|
|
|
|
|
|
|
aLastNetAttributes = full_attribute_string;
|
|
|
|
|
|
|
|
if( clearDict )
|
|
|
|
aPrintedText = full_attribute_string;
|
|
|
|
else
|
|
|
|
aPrintedText = short_attribute_string;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2019-09-15 15:08:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
/************ class GBR_CMP_PNP_METADATA *************/
|
|
|
|
|
|
|
|
void GBR_CMP_PNP_METADATA::ClearData()
|
|
|
|
{
|
|
|
|
// Clear all strings
|
|
|
|
m_Orientation = 0.0;
|
|
|
|
m_Manufacturer.Clear();
|
|
|
|
m_MPN.Clear();
|
|
|
|
m_Package.Clear();
|
|
|
|
m_Value.Clear();
|
|
|
|
m_MountType = MOUNT_TYPE_UNSPECIFIED;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @return a string containing the formated metadata in X2 syntax.
|
|
|
|
* one line by non empty data
|
2019-10-24 16:50:51 +00:00
|
|
|
* the orientation (.CRot) and mount type (.CMnt) are always generated
|
2019-09-15 15:08:57 +00:00
|
|
|
*/
|
|
|
|
wxString GBR_CMP_PNP_METADATA::FormatCmpPnPMetadata()
|
|
|
|
{
|
|
|
|
wxString text;
|
|
|
|
wxString start_of_line( "%TO.");
|
|
|
|
wxString end_of_line( "*%\n" );
|
|
|
|
|
|
|
|
wxString mounType[] =
|
|
|
|
{
|
2019-11-10 13:29:08 +00:00
|
|
|
"Other", "SMD", "TH"
|
2019-09-15 15:08:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if( !m_Manufacturer.IsEmpty() )
|
|
|
|
text << start_of_line << "CMfr," << m_Manufacturer << end_of_line;
|
|
|
|
|
|
|
|
if( !m_MPN.IsEmpty() )
|
|
|
|
text << start_of_line << "CMPN," << m_MPN << end_of_line;
|
|
|
|
|
|
|
|
if( !m_Package.IsEmpty() )
|
|
|
|
text << start_of_line << "Cpkg," << m_Package << end_of_line;
|
|
|
|
|
|
|
|
if( !m_Footprint.IsEmpty() )
|
|
|
|
text << start_of_line << "CFtp," << m_Footprint << end_of_line;
|
|
|
|
|
|
|
|
if( !m_Value.IsEmpty() )
|
|
|
|
text << start_of_line << "CVal," << m_Value << end_of_line;
|
|
|
|
|
2019-10-06 11:39:50 +00:00
|
|
|
if( !m_LibraryName.IsEmpty() )
|
|
|
|
text << start_of_line << "CLbN," << m_LibraryName << end_of_line;
|
|
|
|
|
|
|
|
if( !m_LibraryDescr.IsEmpty() )
|
|
|
|
text << start_of_line << "CLbD," << m_LibraryDescr << end_of_line;
|
|
|
|
|
2019-09-15 15:08:57 +00:00
|
|
|
text << start_of_line << "CMnt," << mounType[m_MountType] << end_of_line;
|
|
|
|
text << start_of_line << "CRot," << m_Orientation << end_of_line;
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|