kicad/common/gbr_metadata.cpp

376 lines
13 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2018 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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>
#include <gbr_metadata.h>
std::string GBR_APERTURE_METADATA::FormatAttribute( GBR_APERTURE_ATTRIB aAttribute,
bool aUseX1StructuredComment )
{
std::string attribute_string;
// 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)
attribute_string = "TA.AperFunction,EtchedComponent";
break;
case GBR_APERTURE_ATTRIB_CONDUCTOR: // print info associated to a track
attribute_string = "TA.AperFunction,Conductor";
break;
case GBR_APERTURE_ATTRIB_CUTOUT: // print info associated to a outline
attribute_string = "TA.AperFunction,CutOut";
break;
case GBR_APERTURE_ATTRIB_VIAPAD: // print info associated to a flashed via
attribute_string = "TA.AperFunction,ViaPad";
break;
case GBR_APERTURE_ATTRIB_NONCONDUCTOR: // print info associated to a item on a copper layer
// which is not a track (for instance a text)
attribute_string = "TA.AperFunction,NonConductor";
break;
case GBR_APERTURE_ATTRIB_COMPONENTPAD: // print info associated to a flashed
// through hole component on outer layer
attribute_string = "TA.AperFunction,ComponentPad";
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
attribute_string = "TA.AperFunction,SMDPad,SMDef";
break;
case GBR_APERTURE_ATTRIB_SMDPAD_CUDEF: // print info associated to a flashed SMD pad with
// a solder mask defined by the solder mask
attribute_string = "TA.AperFunction,SMDPad,CuDef";
break;
case GBR_APERTURE_ATTRIB_BGAPAD_SMDEF: // print info associated to flashed BGA pads with
// a solder mask defined by the copper shape
attribute_string = "TA.AperFunction,BGAPad,SMDef";
break;
case GBR_APERTURE_ATTRIB_BGAPAD_CUDEF: // print info associated to a flashed BGA pad with
// a solder mask defined by the solder mask
attribute_string = "TA.AperFunction,BGAPad,CuDef";
break;
case GBR_APERTURE_ATTRIB_CONNECTORPAD: // print info associated to a flashed edge connector pad (outer layers)
attribute_string = "TA.AperFunction,ConnectorPad";
break;
case GBR_APERTURE_ATTRIB_WASHERPAD: // print info associated to flashed mechanical pads (NPTH)
attribute_string = "TA.AperFunction,WasherPad";
break;
case GBR_APERTURE_ATTRIB_HEATSINKPAD: // print info associated to a flashed heat sink pad
// (typically for SMDs)
attribute_string = "TA.AperFunction,HeatsinkPad";
break;
case GBR_APERTURE_ATTRIB_VIADRILL: // print info associated to a via hole in drill files
attribute_string = "TA.AperFunction,ViaDrill";
break;
case GBR_APERTURE_ATTRIB_COMPONENTDRILL: // print info associated to a component
// round hole in drill files
attribute_string = "TA.AperFunction,ComponentDrill";
break;
case GBR_APERTURE_ATTRIB_SLOTDRILL: // print info associated to a oblong hole in drill files
attribute_string = "TA.AperFunction,Slot";
break;
}
std::string full_attribute_string;
wxString eol_string;
if( !attribute_string.empty() )
{
if( aUseX1StructuredComment )
{
full_attribute_string = "G04 #@! ";
eol_string = "*\n";
}
else
{
full_attribute_string = "%";
eol_string = "*%\n";
}
}
full_attribute_string += attribute_string + eol_string;
return full_attribute_string;
}
wxString FormatStringFromGerber( const wxString& aString )
{
// make the inverse conversion of formatStringToGerber()
// It converts a "normalized" gerber string and convert it to a 16 bits sequence unicode
// and return a wxString (unicode 16) from the gerber string
wxString txt;
for( unsigned ii = 0; ii < aString.Length(); ++ii )
{
unsigned code = aString[ii];
if( code == '\\' )
{
// Convert 4 hexadecimal digits to a 16 bit unicode
// (Gerber allows only 4 hexadecimal digits)
long value = 0;
for( int jj = 0; jj < 4; jj++ )
{
value <<= 4;
code = aString[++ii];
// Very basic conversion, but it expects a valid gerber file
int hexa = (code <= '9' ? code - '0' : code - 'A' + 10) & 0xF;
value += hexa;
}
txt.Append( wxChar( value ) );
}
else
txt.Append( aString[ii] );
}
return txt;
}
std::string formatStringToGerber( const wxString& aString )
{
/* format string means convert any code > 0x7F and unautorized code to a hexadecimal
* 16 bits sequence unicode
* unautorized codes are ',' '*' '%' '\'
*/
std::string txt;
txt.reserve( aString.Length() );
for( unsigned ii = 0; ii < aString.Length(); ++ii )
{
unsigned code = aString[ii];
bool convert = false;
switch( code )
{
case '\\':
case '%':
case '*':
case ',':
convert = true;
break;
default:
break;
}
if( convert || code > 0x7F )
{
txt += '\\';
// Convert code to 4 hexadecimal digit
// (Gerber allows only 4 hexadecimal digit)
char hexa[32];
sprintf( hexa,"%4.4X", code & 0xFFFF);
txt += hexa;
}
else
txt += char( code );
}
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,
GBR_NETLIST_METADATA* aData, bool& aClearPreviousAttributes,
bool aUseX1StructuredComment )
{
aClearPreviousAttributes = false;
wxString prepend_string;
wxString eol_string;
if( aUseX1StructuredComment )
{
prepend_string = "G04 #@! ";
eol_string = "*\n";
}
else
{
prepend_string = "%";
eol_string = "*%\n";
}
// print a Gerber net attribute record.
// it is added to the object attributes dictionnary
// 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 ) )
{
// print info associated to a flashed pad (cmpref, pad name)
// example: %TO.P,R5,3*%
pad_attribute_string = prepend_string + "TO.P,";
pad_attribute_string += formatStringToGerber( aData->m_Cmpref ) + ",";
if( aData->m_Padname.IsEmpty() )
// Happens for "mechanical" or never connected pads
pad_attribute_string += formatStringToGerber( NO_PAD_NAME );
else
pad_attribute_string += formatStringToGerber( aData->m_Padname );
pad_attribute_string += eol_string;
}
if( ( aData->m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_NET ) )
{
// print info associated to a net
// example: %TO.N,Clk3*%
net_attribute_string = prepend_string + "TO.N,";
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
net_attribute_string += formatStringToGerber( NO_NET_NAME );
}
}
else
net_attribute_string += formatStringToGerber( aData->m_Netname );
net_attribute_string += eol_string;
}
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
cmp_attribute_string = prepend_string + "TO.C,";
cmp_attribute_string += formatStringToGerber( aData->m_Cmpref ) + eol_string;
}
// 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;
if( aLastNetAttributes != full_attribute_string )
{
// first, remove no more existing attributes.
// Because in Kicad the full attribute list is evaluated for each object,
// the entire dictionnary is cleared
bool clearDict = false;
if( aLastNetAttributes.find( "TO.P," ) != std::string::npos )
{
if( pad_attribute_string.empty() ) // No more this attribute
clearDict = true;
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;
if( aLastNetAttributes.find( "TO.N," ) != std::string::npos )
{
if( net_attribute_string.empty() ) // No more this attribute
clearDict = true;
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;
if( aLastNetAttributes.find( "TO.C," ) != std::string::npos )
{
if( cmp_attribute_string.empty() ) // No more this attribute
clearDict = true;
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;
}