2012-06-09 09:38:58 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2017-06-02 18:14:37 +00:00
|
|
|
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
2012-06-09 09:38:58 +00:00
|
|
|
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
2017-06-02 18:14:37 +00:00
|
|
|
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
|
2012-06-09 09:38:58 +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
|
|
|
|
*/
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
/**
|
|
|
|
* @file pcbnew/pcbplot.cpp
|
|
|
|
*/
|
|
|
|
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <fctsys.h>
|
2018-01-28 18:12:26 +00:00
|
|
|
#include <plotter.h>
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <confirm.h>
|
|
|
|
#include <wxPcbStruct.h>
|
|
|
|
#include <pcbplot.h>
|
2012-04-13 18:51:24 +00:00
|
|
|
#include <base_units.h>
|
2013-05-10 19:22:29 +00:00
|
|
|
#include <reporter.h>
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <class_board.h>
|
|
|
|
#include <pcbnew.h>
|
2012-08-29 16:59:50 +00:00
|
|
|
#include <plotcontroller.h>
|
2012-09-20 18:58:41 +00:00
|
|
|
#include <pcb_plot_params.h>
|
2012-08-29 16:59:50 +00:00
|
|
|
#include <wx/ffile.h>
|
2012-09-15 12:13:03 +00:00
|
|
|
#include <dialog_plot.h>
|
2013-05-02 18:06:58 +00:00
|
|
|
#include <macros.h>
|
2015-03-25 13:07:05 +00:00
|
|
|
#include <build_version.h>
|
2007-05-06 16:03:28 +00:00
|
|
|
|
2011-02-03 22:26:30 +00:00
|
|
|
|
2015-10-20 18:03:07 +00:00
|
|
|
const wxString GetGerberProtelExtension( LAYER_NUM aLayer )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
2014-06-24 16:17:18 +00:00
|
|
|
if( IsCopperLayer( aLayer ) )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
2014-06-24 16:17:18 +00:00
|
|
|
if( aLayer == F_Cu )
|
|
|
|
return wxT( "gtl" );
|
|
|
|
else if( aLayer == B_Cu )
|
|
|
|
return wxT( "gbl" );
|
|
|
|
else
|
|
|
|
{
|
2015-06-24 17:59:44 +00:00
|
|
|
return wxString::Format( wxT( "g%d" ), aLayer+1 );
|
2014-06-24 16:17:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch( aLayer )
|
|
|
|
{
|
|
|
|
case B_Adhes: return wxT( "gba" );
|
|
|
|
case F_Adhes: return wxT( "gta" );
|
|
|
|
|
|
|
|
case B_Paste: return wxT( "gbp" );
|
|
|
|
case F_Paste: return wxT( "gtp" );
|
|
|
|
|
|
|
|
case B_SilkS: return wxT( "gbo" );
|
|
|
|
case F_SilkS: return wxT( "gto" );
|
|
|
|
|
|
|
|
case B_Mask: return wxT( "gbs" );
|
|
|
|
case F_Mask: return wxT( "gts" );
|
|
|
|
|
2015-06-24 17:59:44 +00:00
|
|
|
case Edge_Cuts: return wxT( "gm1" );
|
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
case Dwgs_User:
|
|
|
|
case Cmts_User:
|
|
|
|
case Eco1_User:
|
|
|
|
case Eco2_User:
|
|
|
|
default: return wxT( "gbr" );
|
|
|
|
}
|
2012-08-29 16:59:50 +00:00
|
|
|
}
|
2013-05-10 19:22:29 +00:00
|
|
|
}
|
2012-08-29 16:59:50 +00:00
|
|
|
|
2016-11-30 16:48:31 +00:00
|
|
|
|
|
|
|
const wxString GetGerberFileFunctionAttribute( const BOARD *aBoard, LAYER_NUM aLayer )
|
2014-06-19 06:26:53 +00:00
|
|
|
{
|
2014-10-31 18:59:37 +00:00
|
|
|
wxString attrib;
|
2014-06-19 06:26:53 +00:00
|
|
|
|
|
|
|
switch( aLayer )
|
|
|
|
{
|
2014-06-30 06:44:46 +00:00
|
|
|
case F_Adhes:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Glue,Top";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case B_Adhes:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Glue,Bot";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case F_SilkS:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Legend,Top";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case B_SilkS:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Legend,Bot";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case F_Mask:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Soldermask,Top";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case B_Mask:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Soldermask,Bot";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case F_Paste:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Paste,Top";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case B_Paste:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Paste,Bot";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case Edge_Cuts:
|
2014-07-04 14:22:38 +00:00
|
|
|
// Board outline.
|
|
|
|
// Can be "Profile,NP" (Not Plated: usual) or "Profile,P"
|
|
|
|
// This last is the exception (Plated)
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Profile,NP";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case Dwgs_User:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Drawing";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case Cmts_User:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Other,Comment";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case Eco1_User:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Other,ECO1";
|
2014-06-30 06:44:46 +00:00
|
|
|
break;
|
|
|
|
|
2014-07-01 19:20:38 +00:00
|
|
|
case Eco2_User:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Other,ECO2";
|
2014-06-30 06:44:46 +00:00
|
|
|
break;
|
|
|
|
|
2015-03-25 13:07:05 +00:00
|
|
|
case B_Fab:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Other,Fab,Bot";
|
2015-03-25 13:07:05 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case F_Fab:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Other,Fab,Top";
|
2015-03-25 13:07:05 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
case B_Cu:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib.Printf( wxT( "Copper,L%d,Bot" ), aBoard->GetCopperLayerCount() );
|
2014-06-30 06:44:46 +00:00
|
|
|
break;
|
|
|
|
|
2014-07-01 19:20:38 +00:00
|
|
|
case F_Cu:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib = "Copper,L1,Top";
|
2014-07-29 16:38:27 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
default:
|
|
|
|
if( IsCopperLayer( aLayer ) )
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib.Printf( wxT( "Copper,L%d,Inr" ), aLayer+1 );
|
2015-03-25 13:07:05 +00:00
|
|
|
else
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib.Printf( wxT( "Other,User" ), aLayer+1 );
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the signal type of the layer, if relevant
|
2014-06-30 06:44:46 +00:00
|
|
|
if( IsCopperLayer( aLayer ) )
|
|
|
|
{
|
|
|
|
LAYER_T type = aBoard->GetLayerType( ToLAYER_ID( aLayer ) );
|
|
|
|
|
2014-06-19 06:26:53 +00:00
|
|
|
switch( type )
|
|
|
|
{
|
|
|
|
case LT_SIGNAL:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib += ",Signal";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
case LT_POWER:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib += ",Plane";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
case LT_MIXED:
|
2016-09-19 11:01:36 +00:00
|
|
|
attrib += ",Mixed";
|
2014-06-19 06:26:53 +00:00
|
|
|
break;
|
|
|
|
default:
|
2014-10-31 18:59:37 +00:00
|
|
|
break; // do nothing (but avoid a warning for unhandled LAYER_T values from GCC)
|
2014-06-19 06:26:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-31 18:59:37 +00:00
|
|
|
wxString fileFct;
|
2016-11-30 16:48:31 +00:00
|
|
|
fileFct.Printf( "%%TF.FileFunction,%s*%%", GetChars( attrib ) );
|
2014-10-31 18:59:37 +00:00
|
|
|
|
|
|
|
return fileFct;
|
2014-06-19 06:26:53 +00:00
|
|
|
}
|
|
|
|
|
2016-09-19 11:01:36 +00:00
|
|
|
|
|
|
|
static const wxString GetGerberFilePolarityAttribute( LAYER_NUM aLayer )
|
|
|
|
{
|
|
|
|
/* build the string %TF.FilePolarity,Positive*%
|
|
|
|
* or %TF.FilePolarity,Negative*%
|
|
|
|
* an emply string for layers which do not use a polarity
|
|
|
|
*
|
|
|
|
* The value of the .FilePolarity specifies whether the image represents the
|
|
|
|
* presence or absence of material.
|
|
|
|
* This attribute can only be used when the file represents a pattern in a material layer,
|
|
|
|
* e.g. copper, solder mask, legend.
|
|
|
|
* Together with.FileFunction it defines the role of that image in
|
|
|
|
* the layer structure of the PCB.
|
|
|
|
* Note that the .FilePolarity attribute does not change the image -
|
|
|
|
* no attribute does.
|
|
|
|
* It changes the interpretation of the image.
|
|
|
|
* For example, in a copper layer in positive polarity a round flash generates a copper pad.
|
|
|
|
* In a copper layer in negative polarity it generates a clearance.
|
|
|
|
* Solder mask images usually represent solder mask openings and are then negative.
|
|
|
|
* This may be counter-intuitive.
|
|
|
|
*/
|
|
|
|
int polarity = 0;
|
|
|
|
|
|
|
|
switch( aLayer )
|
|
|
|
{
|
|
|
|
case F_Adhes:
|
|
|
|
case B_Adhes:
|
|
|
|
case F_SilkS:
|
|
|
|
case B_SilkS:
|
|
|
|
case F_Paste:
|
|
|
|
case B_Paste:
|
|
|
|
polarity = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case F_Mask:
|
|
|
|
case B_Mask:
|
|
|
|
polarity = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if( IsCopperLayer( aLayer ) )
|
|
|
|
polarity = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString filePolarity;
|
|
|
|
|
|
|
|
if( polarity == 1 )
|
|
|
|
filePolarity = "%TF.FilePolarity,Positive*%";
|
|
|
|
if( polarity == -1 )
|
|
|
|
filePolarity = "%TF.FilePolarity,Negative*%";
|
|
|
|
|
|
|
|
return filePolarity;
|
|
|
|
}
|
|
|
|
|
2015-03-25 13:07:05 +00:00
|
|
|
/* Add some X2 attributes to the file header, as defined in the
|
2015-10-14 07:20:28 +00:00
|
|
|
* Gerber file format specification J4 and "Revision 2015.06"
|
2015-03-25 13:07:05 +00:00
|
|
|
*/
|
2016-11-30 16:48:31 +00:00
|
|
|
|
|
|
|
// A helper function to convert a X2 attribute string to a X1 structured comment:
|
|
|
|
static wxString& makeStringCompatX1( wxString& aText, bool aUseX1CompatibilityMode )
|
|
|
|
{
|
|
|
|
if( aUseX1CompatibilityMode )
|
|
|
|
{
|
|
|
|
aText.Replace( "%", "" );
|
|
|
|
aText.Prepend( "G04 #@! " );
|
|
|
|
}
|
|
|
|
|
|
|
|
return aText;
|
|
|
|
}
|
|
|
|
|
2017-04-28 10:53:30 +00:00
|
|
|
|
2017-08-19 16:28:11 +00:00
|
|
|
void BuildGerberX2Header( const BOARD *aBoard, wxArrayString& aHeader )
|
|
|
|
{
|
|
|
|
wxString text;
|
|
|
|
|
|
|
|
// Creates the TF,.GenerationSoftware. Format is:
|
|
|
|
// %TF,.GenerationSoftware,<vendor>,<application name>[,<application version>]*%
|
|
|
|
text.Printf( wxT( "%%TF.GenerationSoftware,KiCad,Pcbnew,%s*%%" ), GetBuildVersion() );
|
|
|
|
aHeader.Add( text );
|
|
|
|
|
|
|
|
// creates the TF.CreationDate ext:
|
|
|
|
// 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 msg = date.Format( wxT( "%z" ) ); // Extract the time zone offset
|
|
|
|
// The time zone offset format is + (or -) mm or hhmm (mm = number of minutes, hh = number of hours)
|
|
|
|
// we want +(or -) hh:mm
|
|
|
|
if( msg.Len() > 3 )
|
|
|
|
msg.insert( 3, ":", 1 ),
|
|
|
|
text.Printf( wxT( "%%TF.CreationDate,%s%s*%%" ), GetChars( date.FormatISOCombined() ), GetChars( msg ) );
|
|
|
|
aHeader.Add( text );
|
|
|
|
|
|
|
|
// Creates the TF,.ProjectId. Format is (from Gerber file format doc):
|
|
|
|
// %TF.ProjectId,<project id>,<project GUID>,<revision id>*%
|
|
|
|
// <project id> is the name of the project, restricted to basic ASCII symbols only,
|
|
|
|
// and comma not accepted
|
|
|
|
// All illegal chars will be replaced by underscore
|
|
|
|
// <project GUID> is a 32 hexadecimal digits string which is an unique id of a project.
|
|
|
|
// This is a random 128-bit number expressed in 32 hexadecimal digits.
|
|
|
|
// See en.wikipedia.org/wiki/GUID for more information
|
|
|
|
// However Kicad does not handle such a project GUID, so it is built from the board name
|
|
|
|
// Rem: <project id> accepts only ASCII 7 code (only basic ASCII codes are allowed in gerber files).
|
|
|
|
wxFileName fn = aBoard->GetFileName();
|
|
|
|
msg = fn.GetFullName();
|
|
|
|
wxString guid;
|
|
|
|
|
|
|
|
// Build a 32 digits GUID from the board name:
|
|
|
|
for( unsigned ii = 0; ii < msg.Len(); ii++ )
|
|
|
|
{
|
|
|
|
int cc1 = int( msg[ii] ) & 0x0F;
|
|
|
|
int cc2 = ( int( msg[ii] ) >> 4) & 0x0F;
|
|
|
|
guid << wxString::Format( wxT( "%X%X" ), cc2, cc1 );
|
|
|
|
|
|
|
|
if( guid.Len() >= 32 )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// guid has 32 digits, so add missing digits
|
|
|
|
int cnt = 32 - guid.Len();
|
|
|
|
|
|
|
|
if( cnt > 0 )
|
|
|
|
guid.Append( '0', cnt );
|
|
|
|
|
|
|
|
// build the <project id> string: this is the board short filename (without ext)
|
|
|
|
// and all non ASCII chars and comma are replaced by '_'
|
|
|
|
msg = fn.GetName();
|
|
|
|
msg.Replace( wxT( "," ), wxT( "_" ) );
|
|
|
|
|
|
|
|
// build the <rec> string. All non ASCII chars and comma are replaced by '_'
|
|
|
|
wxString rev = ((BOARD*)aBoard)->GetTitleBlock().GetRevision();
|
|
|
|
rev.Replace( wxT( "," ), wxT( "_" ) );
|
|
|
|
|
|
|
|
if( rev.IsEmpty() )
|
|
|
|
rev = wxT( "rev?" );
|
|
|
|
|
|
|
|
text.Printf( wxT( "%%TF.ProjectId,%s,%s,%s*%%" ), msg.ToAscii(), GetChars( guid ), rev.ToAscii() );
|
|
|
|
aHeader.Add( text );
|
|
|
|
|
|
|
|
// Add the TF.SameCoordinates, that specify all gerber files uses the same
|
|
|
|
// origin and orientation, and the registration between files is OK.
|
|
|
|
// The parameter of TF.SameCoordinates is a string that is common
|
|
|
|
// to all files using the same registration and has no special meaning:
|
|
|
|
// this is just a key
|
|
|
|
// Because there is no mirroring/rotation in Kicad, only the plot offset origin
|
|
|
|
// can create incorrect registration.
|
|
|
|
// So we create a key from plot offset options.
|
|
|
|
// and therefore for a given board, all Gerber files having the same key have the same
|
|
|
|
// plot origin and use the same registration
|
|
|
|
//
|
|
|
|
// Currently the key is "Original" when using absolute Pcbnew coordinates,
|
|
|
|
// and te PY ans PY position od auxiliary axis, when using it.
|
|
|
|
// Please, if absolute Pcbnew coordinates, one day, are set by user, change the way
|
|
|
|
// the key is built to ensure file only using the *same* axis have the same key.
|
|
|
|
wxString registration_id = "Original";
|
|
|
|
wxPoint auxOrigin = aBoard->GetAuxOrigin();
|
|
|
|
|
|
|
|
if( aBoard->GetPlotOptions().GetUseAuxOrigin() && auxOrigin.x && auxOrigin.y )
|
|
|
|
registration_id.Printf( "PX%xPY%x", auxOrigin.x, auxOrigin.y );
|
|
|
|
|
|
|
|
text.Printf( "%%TF.SameCoordinates,%s*%%", registration_id.GetData() );
|
|
|
|
aHeader.Add( text );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-28 10:53:30 +00:00
|
|
|
void AddGerberX2Header( PLOTTER * aPlotter,
|
|
|
|
const BOARD *aBoard, bool aUseX1CompatibilityMode )
|
2015-03-25 13:07:05 +00:00
|
|
|
{
|
|
|
|
wxString text;
|
|
|
|
|
|
|
|
// Creates the TF,.GenerationSoftware. Format is:
|
|
|
|
// %TF,.GenerationSoftware,<vendor>,<application name>[,<application version>]*%
|
2015-09-07 18:09:41 +00:00
|
|
|
text.Printf( wxT( "%%TF.GenerationSoftware,KiCad,Pcbnew,%s*%%" ), GetBuildVersion() );
|
2016-11-30 16:48:31 +00:00
|
|
|
aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
|
2015-03-25 13:07:05 +00:00
|
|
|
|
|
|
|
// creates the TF.CreationDate ext:
|
|
|
|
// 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 msg = date.Format( wxT( "%z" ) ); // Extract the time zone offset
|
|
|
|
// The time zone offset format is + (or -) mm or hhmm (mm = number of minutes, hh = number of hours)
|
|
|
|
// we want +(or -) hh:mm
|
|
|
|
if( msg.Len() > 3 )
|
|
|
|
msg.insert( 3, ":", 1 ),
|
2015-09-07 18:09:41 +00:00
|
|
|
text.Printf( wxT( "%%TF.CreationDate,%s%s*%%" ), GetChars( date.FormatISOCombined() ), GetChars( msg ) );
|
2016-11-30 16:48:31 +00:00
|
|
|
aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
|
2015-03-25 13:07:05 +00:00
|
|
|
|
2015-10-14 07:20:28 +00:00
|
|
|
// Creates the TF,.ProjectId. Format is (from Gerber file format doc):
|
|
|
|
// %TF.ProjectId,<project id>,<project GUID>,<revision id>*%
|
2015-03-25 13:07:05 +00:00
|
|
|
// <project id> is the name of the project, restricted to basic ASCII symbols only,
|
|
|
|
// and comma not accepted
|
|
|
|
// All illegal chars will be replaced by underscore
|
|
|
|
// <project GUID> is a 32 hexadecimal digits string which is an unique id of a project.
|
|
|
|
// This is a random 128-bit number expressed in 32 hexadecimal digits.
|
|
|
|
// See en.wikipedia.org/wiki/GUID for more information
|
|
|
|
// However Kicad does not handle such a project GUID, so it is built from the board name
|
|
|
|
// Rem: <project id> accepts only ASCII 7 code (only basic ASCII codes are allowed in gerber files).
|
|
|
|
wxFileName fn = aBoard->GetFileName();
|
|
|
|
msg = fn.GetFullName();
|
|
|
|
wxString guid;
|
|
|
|
|
|
|
|
// Build a 32 digits GUID from the board name:
|
|
|
|
for( unsigned ii = 0; ii < msg.Len(); ii++ )
|
|
|
|
{
|
|
|
|
int cc1 = int( msg[ii] ) & 0x0F;
|
|
|
|
int cc2 = ( int( msg[ii] ) >> 4) & 0x0F;
|
|
|
|
guid << wxString::Format( wxT( "%X%X" ), cc2, cc1 );
|
|
|
|
|
|
|
|
if( guid.Len() >= 32 )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// guid has 32 digits, so add missing digits
|
|
|
|
int cnt = 32 - guid.Len();
|
|
|
|
|
|
|
|
if( cnt > 0 )
|
|
|
|
guid.Append( '0', cnt );
|
|
|
|
|
|
|
|
// build the <project id> string: this is the board short filename (without ext)
|
|
|
|
// and all non ASCII chars and comma are replaced by '_'
|
|
|
|
msg = fn.GetName();
|
|
|
|
msg.Replace( wxT( "," ), wxT( "_" ) );
|
|
|
|
|
|
|
|
// build the <rec> string. All non ASCII chars and comma are replaced by '_'
|
|
|
|
wxString rev = ((BOARD*)aBoard)->GetTitleBlock().GetRevision();
|
|
|
|
rev.Replace( wxT( "," ), wxT( "_" ) );
|
|
|
|
|
|
|
|
if( rev.IsEmpty() )
|
|
|
|
rev = wxT( "rev?" );
|
|
|
|
|
2015-10-14 07:20:28 +00:00
|
|
|
text.Printf( wxT( "%%TF.ProjectId,%s,%s,%s*%%" ), msg.ToAscii(), GetChars( guid ), rev.ToAscii() );
|
2016-11-30 16:48:31 +00:00
|
|
|
aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
|
2017-06-02 18:14:37 +00:00
|
|
|
|
|
|
|
// Add the TF.SameCoordinates, that specify all gerber files uses the same
|
|
|
|
// origin and orientation, and the registration between files is OK.
|
|
|
|
// The parameter of TF.SameCoordinates is a string that is common
|
|
|
|
// to all files using the same registration and has no special meaning:
|
|
|
|
// this is just a key
|
|
|
|
// Because there is no mirroring/rotation in Kicad, only the plot offset origin
|
|
|
|
// can create incorrect registration.
|
|
|
|
// So we create a key from plot offset options.
|
|
|
|
// and therefore for a given board, all Gerber files having the same key have the same
|
|
|
|
// plot origin and use the same registration
|
|
|
|
//
|
|
|
|
// Currently the key is "Original" when using absolute Pcbnew coordinates,
|
|
|
|
// and te PY ans PY position od auxiliary axis, when using it.
|
|
|
|
// Please, if absolute Pcbnew coordinates, one day, are set by user, change the way
|
|
|
|
// the key is built to ensure file only using the *same* axis have the same key.
|
|
|
|
wxString registration_id = "Original";
|
|
|
|
wxPoint auxOrigin = aBoard->GetAuxOrigin();
|
|
|
|
|
|
|
|
if( aBoard->GetPlotOptions().GetUseAuxOrigin() && auxOrigin.x && auxOrigin.y )
|
|
|
|
registration_id.Printf( "PX%xPY%x", auxOrigin.x, auxOrigin.y );
|
|
|
|
|
2017-07-25 09:50:24 +00:00
|
|
|
text.Printf( "%%TF.SameCoordinates,%s*%%", registration_id.GetData() );
|
2017-06-02 18:14:37 +00:00
|
|
|
aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
|
2017-04-28 10:53:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AddGerberX2Attribute( PLOTTER * aPlotter,
|
|
|
|
const BOARD *aBoard, LAYER_NUM aLayer, bool aUseX1CompatibilityMode )
|
|
|
|
{
|
|
|
|
AddGerberX2Header( aPlotter, aBoard, aUseX1CompatibilityMode );
|
|
|
|
|
|
|
|
wxString text;
|
2015-03-25 13:07:05 +00:00
|
|
|
|
|
|
|
// Add the TF.FileFunction
|
2016-11-30 16:48:31 +00:00
|
|
|
text = GetGerberFileFunctionAttribute( aBoard, aLayer );
|
|
|
|
aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
|
2016-09-19 11:01:36 +00:00
|
|
|
|
|
|
|
// Add the TF.FilePolarity (for layers which support that)
|
|
|
|
text = GetGerberFilePolarityAttribute( aLayer );
|
|
|
|
|
|
|
|
if( !text.IsEmpty() )
|
2016-11-30 16:48:31 +00:00
|
|
|
aPlotter->AddLineToHeader( makeStringCompatX1( text, aUseX1CompatibilityMode ) );
|
2015-03-25 13:07:05 +00:00
|
|
|
}
|
|
|
|
|
2014-06-30 06:44:46 +00:00
|
|
|
|
2015-10-20 18:03:07 +00:00
|
|
|
void BuildPlotFileName( wxFileName* aFilename, const wxString& aOutputDir,
|
|
|
|
const wxString& aSuffix, const wxString& aExtension )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
2015-10-20 18:03:07 +00:00
|
|
|
// aFilename contains the base filename only (without path and extension)
|
|
|
|
// when calling this function.
|
|
|
|
// It is expected to be a valid filename (this is usually the board filename)
|
2012-08-29 16:59:50 +00:00
|
|
|
aFilename->SetPath( aOutputDir );
|
|
|
|
|
|
|
|
// Set the file extension
|
|
|
|
aFilename->SetExt( aExtension );
|
|
|
|
|
2015-10-20 18:03:07 +00:00
|
|
|
// remove leading and trailing spaces if any from the suffix, if
|
|
|
|
// something survives add it to the name;
|
|
|
|
// also the suffix can contain some not allowed chars in filename (/ \ . :),
|
|
|
|
// so change them to underscore
|
|
|
|
// Remember it can be called from a python script, so the illegal chars
|
|
|
|
// have to be filtered here.
|
2012-11-15 10:47:23 +00:00
|
|
|
wxString suffix = aSuffix;
|
|
|
|
suffix.Trim( true );
|
|
|
|
suffix.Trim( false );
|
|
|
|
|
2015-10-20 18:03:07 +00:00
|
|
|
wxString badchars = wxFileName::GetForbiddenChars(wxPATH_DOS);
|
|
|
|
badchars.Append( '%' );
|
|
|
|
|
|
|
|
for( unsigned ii = 0; ii < badchars.Len(); ii++ )
|
|
|
|
suffix.Replace( badchars[ii], wxT("_") );
|
2012-11-15 10:47:23 +00:00
|
|
|
|
|
|
|
if( !suffix.IsEmpty() )
|
|
|
|
aFilename->SetName( aFilename->GetName() + wxT( "-" ) + suffix );
|
|
|
|
}
|
2012-08-29 16:59:50 +00:00
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
PLOT_CONTROLLER::PLOT_CONTROLLER( BOARD *aBoard )
|
|
|
|
{
|
2015-04-27 18:02:27 +00:00
|
|
|
m_plotter = NULL;
|
|
|
|
m_board = aBoard;
|
|
|
|
m_plotLayer = UNDEFINED_LAYER;
|
2012-08-29 16:59:50 +00:00
|
|
|
}
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
PLOT_CONTROLLER::~PLOT_CONTROLLER()
|
|
|
|
{
|
|
|
|
ClosePlot();
|
|
|
|
}
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
/* IMPORTANT THING TO KNOW: the locale during plots *MUST* be kept as
|
|
|
|
* C/POSIX using a LOCALE_IO object on the stack. This even when
|
|
|
|
* opening/closing the plotfile, since some drivers do I/O even then */
|
|
|
|
|
|
|
|
void PLOT_CONTROLLER::ClosePlot()
|
|
|
|
{
|
|
|
|
LOCALE_IO toggle;
|
|
|
|
|
|
|
|
if( m_plotter )
|
|
|
|
{
|
|
|
|
m_plotter->EndPlot();
|
|
|
|
delete m_plotter;
|
|
|
|
m_plotter = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
|
|
|
bool PLOT_CONTROLLER::OpenPlotfile( const wxString &aSuffix,
|
|
|
|
PlotFormat aFormat,
|
2012-08-29 16:59:50 +00:00
|
|
|
const wxString &aSheetDesc )
|
|
|
|
{
|
|
|
|
LOCALE_IO toggle;
|
|
|
|
|
|
|
|
/* Save the current format: sadly some plot routines depends on this
|
|
|
|
but the main reason is that the StartPlot method uses it to
|
|
|
|
dispatch the plotter creation */
|
2015-04-27 18:02:27 +00:00
|
|
|
GetPlotOptions().SetFormat( aFormat );
|
2012-08-29 16:59:50 +00:00
|
|
|
|
|
|
|
// Ensure that the previous plot is closed
|
|
|
|
ClosePlot();
|
|
|
|
|
|
|
|
// Now compute the full filename for the output and start the plot
|
|
|
|
// (after ensuring the output directory is OK)
|
2015-04-27 18:02:27 +00:00
|
|
|
wxString outputDirName = GetPlotOptions().GetOutputDirectory() ;
|
2012-08-29 16:59:50 +00:00
|
|
|
wxFileName outputDir = wxFileName::DirName( outputDirName );
|
|
|
|
wxString boardFilename = m_board->GetFileName();
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2014-10-26 13:59:01 +00:00
|
|
|
if( EnsureFileDirectoryExists( &outputDir, boardFilename ) )
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
2015-10-26 17:51:41 +00:00
|
|
|
// outputDir contains now the full path of plot files
|
|
|
|
m_plotFile = boardFilename;
|
|
|
|
m_plotFile.SetPath( outputDir.GetPath() );
|
2015-10-20 18:03:07 +00:00
|
|
|
wxString fileExt = GetDefaultPlotExtension( aFormat );
|
|
|
|
|
|
|
|
// Gerber format can use specific file ext, depending on layers
|
|
|
|
// (now not a good practice, because the official file ext is .gbr)
|
|
|
|
if( GetPlotOptions().GetFormat() == PLOT_FORMAT_GERBER &&
|
2015-10-21 06:55:30 +00:00
|
|
|
GetPlotOptions().GetUseGerberProtelExtensions() )
|
2015-10-20 18:03:07 +00:00
|
|
|
fileExt = GetGerberProtelExtension( GetLayer() );
|
|
|
|
|
2015-10-26 17:51:41 +00:00
|
|
|
// Build plot filenames from the board name and layer names:
|
|
|
|
BuildPlotFileName( &m_plotFile, outputDir.GetPath(), aSuffix, fileExt );
|
2012-08-29 16:59:50 +00:00
|
|
|
|
2015-10-20 18:03:07 +00:00
|
|
|
m_plotter = StartPlotBoard( m_board, &GetPlotOptions(), ToLAYER_ID( GetLayer() ),
|
2015-10-26 17:51:41 +00:00
|
|
|
m_plotFile.GetFullPath(), aSheetDesc );
|
2012-08-29 16:59:50 +00:00
|
|
|
}
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
return( m_plotter != NULL );
|
2013-05-10 19:22:29 +00:00
|
|
|
}
|
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
|
2015-04-27 18:02:27 +00:00
|
|
|
bool PLOT_CONTROLLER::PlotLayer()
|
2012-08-29 16:59:50 +00:00
|
|
|
{
|
|
|
|
LOCALE_IO toggle;
|
|
|
|
|
|
|
|
// No plot open, nothing to do...
|
|
|
|
if( !m_plotter )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Fully delegated to the parent
|
2015-04-27 18:02:27 +00:00
|
|
|
PlotOneBoardLayer( m_board, m_plotter, ToLAYER_ID( GetLayer() ), GetPlotOptions() );
|
2012-08-29 16:59:50 +00:00
|
|
|
|
|
|
|
return true;
|
2013-05-10 19:22:29 +00:00
|
|
|
}
|
|
|
|
|
2012-08-29 16:59:50 +00:00
|
|
|
|
2013-04-18 21:21:26 +00:00
|
|
|
void PLOT_CONTROLLER::SetColorMode( bool aColorMode )
|
|
|
|
{
|
|
|
|
if( !m_plotter )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_plotter->SetColorMode( aColorMode );
|
|
|
|
}
|
|
|
|
|
2013-05-10 19:22:29 +00:00
|
|
|
|
2013-04-18 21:21:26 +00:00
|
|
|
bool PLOT_CONTROLLER::GetColorMode()
|
|
|
|
{
|
|
|
|
if( !m_plotter )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return m_plotter->GetColorMode();
|
|
|
|
}
|