You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1192 lines
						
					
					
						
							36 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							1192 lines
						
					
					
						
							36 KiB
						
					
					
				| /* | |
|  * This program source code file is part of KiCad, a free EDA CAD application. | |
|  * | |
|  * Copyright (C) 2007-2016 Jean-Pierre Charras  jp.charras at wanadoo.fr | |
|  * Copyright (C) 1992-2016 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 rs274x.cpp | |
|  */ | |
| 
 | |
| #include <fctsys.h> | |
| #include <common.h> | |
| #include <macros.h> | |
| #include <base_units.h> | |
|  | |
| #include <gerbview.h> | |
| #include <class_gerber_file_image.h> | |
| #include <class_X2_gerber_attributes.h> | |
|  | |
| extern int ReadInt( char*& text, bool aSkipSeparator = true ); | |
| extern double ReadDouble( char*& text, bool aSkipSeparator = true ); | |
| extern bool GetEndOfBlock( char* buff, char*& text, FILE* gerber_file ); | |
| 
 | |
| 
 | |
| #define CODE( x, y ) ( ( (x) << 8 ) + (y) ) | |
|  | |
| // See rs274xrevd_e.pdf, table 1: RS-274X parameters order of entry | |
| // in gerber files, when a coordinate is given (like X78Y600 or I0J80): | |
| //      Y and Y are logical coordinates | |
| //      A and B are plotter coordiantes | |
| //      Usually A = X, B = Y | |
| //      But we can have A = Y, B = X and/or offset, mirror, scale; | |
| // Also: | |
| //  Image is what you must plot (the entire data of the file). | |
| //  Layer is just a set of data blocks with their parameters. An image can have more than one | |
| //   layer so a gerber layer is not like a board layer or the graphic layers used in GerbView | |
| //   to show a file. | |
| enum RS274X_PARAMETERS { | |
|     // Directive parameters: single usage recommended | |
|     // Must be at the beginning of the file | |
|     AXIS_SELECT   = CODE( 'A', 'S' ),           // Default: A=X, B=Y | |
|     FORMAT_STATEMENT = CODE( 'F', 'S' ),        // no default: this command must exists | |
|     MIRROR_IMAGE  = CODE( 'M', 'I' ),           // Default: mo mirror | |
|     MODE_OF_UNITS = CODE( 'M', 'O' ),           // Default:  inch | |
|     INCH   = CODE( 'I', 'N' ), | |
|     MILLIMETER = CODE( 'M', 'M' ), | |
|     OFFSET = CODE( 'O', 'F' ),                  // Default: A = 0, B = 0 | |
|     SCALE_FACTOR   = CODE( 'S', 'F' ),          // Default:  A = 1.0, B = 1.0 | |
|  | |
|     // Image parameters: | |
|     // commands used only once at the beginning of the file | |
|     IMAGE_JUSTIFY  = CODE( 'I', 'J' ),          // Default: no justification | |
|     IMAGE_NAME     = CODE( 'I', 'N' ),          // Default: void | |
|     IMAGE_OFFSET   = CODE( 'I', 'O' ),          // Default: A = 0, B = 0 | |
|     IMAGE_POLARITY = CODE( 'I', 'P' ),          // Default: Positive | |
|     IMAGE_ROTATION = CODE( 'I', 'R' ),          // Default: 0 | |
|     PLOTTER_FILM   = CODE( 'P', 'M' ), | |
| 
 | |
|     // Aperture parameters: | |
|     // Usually for the whole file | |
|     AP_DEFINITION   = CODE( 'A', 'D' ), | |
|     AP_MACRO = CODE( 'A', 'M' ), | |
| 
 | |
|     // X2 extention attribute commands | |
|     // Mainly are found standard attributes and user attributes | |
|     // standard attributes commands are: | |
|     // TF (file attribute) TO (net attribute) | |
|     // TA (aperture attribute) and TD (delete aperture attribute) | |
|     FILE_ATTRIBUTE   = CODE( 'T', 'F' ), | |
| 
 | |
|     // X2 extention Net attribute info | |
|     // Net attribute options are: | |
|     // TO (net attribute data): TO.CN or TO.P TO.N or TO.C | |
|     NET_ATTRIBUTE   = CODE( 'T', 'O' ), | |
| 
 | |
|     // X2 extention Aperture attribute TA | |
|     APERTURE_ATTRIBUTE   = CODE( 'T', 'A' ), | |
| 
 | |
|     // TD (delete aperture/object attribute): | |
|     // Delete aperture attribute added by %TA or Oblect attribute added b %TO | |
|     // TD (delete all) or %TD<attr name> to delete <attr name>. | |
|     // eg: TD.P or TD.N or TD.C ... | |
|     REMOVE_APERTURE_ATTRIBUTE   = CODE( 'T', 'D' ), | |
| 
 | |
|     // Layer specific parameters | |
|     // May be used singly or may be layer specfic | |
|     // theses parameters are at the beginning of the file or layer | |
|     // and reset some layer parameters (like interpolation) | |
|     LAYER_NAME      = CODE( 'L', 'N' ),         // Default: Positive | |
|     LAYER_POLARITY  = CODE( 'L', 'P' ), | |
|     KNOCKOUT = CODE( 'K', 'O' ),                // Default: off | |
|     STEP_AND_REPEAT = CODE( 'S', 'R' ),         //  Default: A = 1, B = 1 | |
|     ROTATE = CODE( 'R', 'O' ),                  //  Default: 0 | |
|  | |
|     // Miscellaneous parameters: | |
|     INCLUDE_FILE   = CODE( 'I', 'F' ) | |
| }; | |
| 
 | |
| 
 | |
| /** | |
|  * Function ReadXCommand | |
|  * reads in two bytes of data and assembles them into an int with the first | |
|  * byte in the sequence put into the most significant part of a 16 bit value | |
|  * and the second byte put into the least significant part of the 16 bit value. | |
|  * @param text A reference to a pointer to read bytes from and to advance as | |
|  *             they are read. | |
|  * @return int - with 16 bits of data in the ls bits, upper bits zeroed. | |
|  */ | |
| static int ReadXCommand( char*& text ) | |
| { | |
|     int result; | |
|     int currbyte; | |
| 
 | |
|     if( text && *text ) | |
|     { | |
|         currbyte = *text++; | |
|         result = ( currbyte & 0xFF ) << 8; | |
|     } | |
|     else | |
|         return -1; | |
| 
 | |
|     if( text && *text ) | |
|     { | |
|         currbyte = *text++; | |
|         result += currbyte & 0xFF; | |
|     } | |
|     else | |
|         return -1; | |
| 
 | |
|     return result; | |
| } | |
| 
 | |
| /** | |
|  * convert a string read from a gerber file to an unicode string | |
|  * usual chars are just copied. \hhhh values are converted to | |
|  * the unicoade char value | |
|  */ | |
| static const wxString fromGerberString( const wxString& aGbrString ) | |
| { | |
|     wxString text; | |
| 
 | |
|     for( unsigned ii = 0; ii < aGbrString.size(); ++ii ) | |
|     { | |
|         if( aGbrString[ii] == '\\' ) | |
|         { | |
|             unsigned value = 0; | |
| 
 | |
|             for( int jj = 0; jj < 4; jj++ ) | |
|             {   // Convert 4 hexa digits to binary value: | |
|                 ii++; | |
|                 value <<= 4; | |
|                 int digit = aGbrString[ii]; | |
| 
 | |
|                 if( digit >= '0' && digit <= '9' ) | |
|                     digit -= '0'; | |
|                 else if( digit >= 'A' && digit <= 'F' ) | |
|                     digit -= 'A' - 10; | |
|                 else if( digit >= 'a' && digit <= 'f' ) | |
|                     digit -= 'a' - 10; | |
|                 else digit = 0; | |
| 
 | |
|                 value += digit & 0xF; | |
|             } | |
| 
 | |
|             text.Append( wxUniChar( value ) ); | |
|         } | |
|         else | |
|             text.Append( aGbrString[ii] ); | |
|     } | |
| 
 | |
|     return text; | |
| } | |
| 
 | |
| bool GERBER_FILE_IMAGE::ReadRS274XCommand( char* buff, char*& text ) | |
| { | |
|     bool ok = true; | |
|     int  code_command; | |
| 
 | |
|     text++; | |
| 
 | |
|     for( ; ; ) | |
|     { | |
|         while( *text ) | |
|         { | |
|             switch( *text ) | |
|             { | |
|             case '%':       // end of command | |
|                 text++; | |
|                 m_CommandState = CMD_IDLE; | |
|                 goto exit;  // success completion | |
|  | |
|             case ' ': | |
|             case '\r': | |
|             case '\n': | |
|                 text++; | |
|                 break; | |
| 
 | |
|             case '*': | |
|                 text++; | |
|                 break; | |
| 
 | |
|             default: | |
|                 code_command = ReadXCommand( text ); | |
|                 ok = ExecuteRS274XCommand( code_command, buff, text ); | |
|                 if( !ok ) | |
|                     goto exit; | |
|                 break; | |
|             } | |
|         } | |
| 
 | |
|         // end of current line, read another one. | |
|         if( fgets( buff, GERBER_BUFZ, m_Current_File ) == NULL ) | |
|         { | |
|             // end of file | |
|             ok = false; | |
|             break; | |
|         } | |
| 
 | |
|         text = buff; | |
|     } | |
| 
 | |
| exit: | |
|     return ok; | |
| } | |
| 
 | |
| 
 | |
| bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int command, char* buff, char*& text ) | |
| { | |
|     int      code; | |
|     int      seq_len;    // not used, just provided | |
|     int      seq_char; | |
|     bool     ok = true; | |
|     char     line[GERBER_BUFZ]; | |
|     wxString msg; | |
|     double   fcoord; | |
|     bool     x_fmt_known = false; | |
|     bool     y_fmt_known = false; | |
| 
 | |
|     // conv_scale = scaling factor from inch to Internal Unit | |
|     double   conv_scale = IU_PER_MILS * 1000; | |
|     if( m_GerbMetric ) | |
|         conv_scale /= 25.4; | |
| 
 | |
| //    DBG( printf( "%22s: Command <%c%c>\n", __func__, (command >> 8) & 0xFF, command & 0xFF ); ) | |
|  | |
|     switch( command ) | |
|     { | |
|     case FORMAT_STATEMENT: | |
|         seq_len = 2; | |
| 
 | |
|         while( *text != '*' ) | |
|         { | |
|             switch( *text ) | |
|             { | |
|             case ' ': | |
|                 text++; | |
|                 break; | |
| 
 | |
|             case 'L':       // No Leading 0 | |
|                 m_DecimalFormat = false; | |
|                 m_NoTrailingZeros = false; | |
|                 text++; | |
|                 break; | |
| 
 | |
|             case 'T':       // No trailing 0 | |
|                 m_DecimalFormat = false; | |
|                 m_NoTrailingZeros = true; | |
|                 text++; | |
|                 break; | |
| 
 | |
|             case 'A':       // Absolute coord | |
|                 m_Relative = false; | |
|                 text++; | |
|                 break; | |
| 
 | |
|             case 'I':       // Relative coord | |
|                 m_Relative = true; | |
|                 text++; | |
|                 break; | |
| 
 | |
|             case 'G': | |
|             case 'N':       // Sequence code (followed by one digit: the sequence len) | |
|                             // (sometimes found before the X,Y sequence) | |
|                             // Obscure option | |
|                 text++; | |
|                 seq_char = *text++; | |
|                 if( (seq_char >= '0') && (seq_char <= '9') ) | |
|                     seq_len = seq_char - '0'; | |
|                 break; | |
| 
 | |
|             case 'D': | |
|             case 'M':       // Sequence code (followed by one digit: the sequence len) | |
|                             // (sometimes found after the X,Y sequence) | |
|                             // Obscure option | |
|                 code = *text++; | |
|                 if( ( *text >= '0' ) && ( *text<= '9' ) ) | |
|                     text++;     // skip the digit | |
|                 else if( code == 'D' ) | |
|                     // Decimal format: sometimes found, but not really documented | |
|                     m_DecimalFormat = true; | |
|                 break; | |
| 
 | |
|             case 'X': | |
|             case 'Y': | |
|             { | |
|                 code = *(text++); | |
|                 char ctmp = *(text++) - '0'; | |
|                 if( code == 'X' ) | |
|                 { | |
|                     x_fmt_known = true; | |
|                     // number of digits after the decimal point (0 to 7 allowed) | |
|                     m_FmtScale.x = *text - '0'; | |
|                     m_FmtLen.x   = ctmp + m_FmtScale.x; | |
| 
 | |
|                     // m_FmtScale is 0 to 7 | |
|                     // (Old Gerber specification was 0 to 6) | |
|                     if( m_FmtScale.x < 0 ) | |
|                         m_FmtScale.x = 0; | |
|                     if( m_FmtScale.x > 7 ) | |
|                         m_FmtScale.x = 7; | |
|                 } | |
|                 else | |
|                 { | |
|                     y_fmt_known = true; | |
|                     m_FmtScale.y = *text - '0'; | |
|                     m_FmtLen.y   = ctmp + m_FmtScale.y; | |
|                     if( m_FmtScale.y < 0 ) | |
|                         m_FmtScale.y = 0; | |
|                     if( m_FmtScale.y > 7 ) | |
|                         m_FmtScale.y = 7; | |
|                 } | |
|                 text++; | |
|             } | |
|             break; | |
| 
 | |
|             case '*': | |
|                 break; | |
| 
 | |
|             default: | |
|                 msg.Printf( wxT( "Unknown id (%c) in FS command" ), | |
|                            *text ); | |
|                 AddMessageToList( msg ); | |
|                 GetEndOfBlock( buff, text, m_Current_File ); | |
|                 ok = false; | |
|                 break; | |
|             } | |
|         } | |
|         if( !x_fmt_known || !y_fmt_known ) | |
|             AddMessageToList( wxT( "RS274X: Format Statement (FS) without X or Y format" ) ); | |
| 
 | |
|         break; | |
| 
 | |
|     case AXIS_SELECT:       // command ASAXBY*% or %ASAYBX*% | |
|         m_SwapAxis = false; | |
|         if( strncasecmp( text, "AYBX", 4 ) == 0 ) | |
|             m_SwapAxis = true; | |
|         break; | |
| 
 | |
|     case MIRROR_IMAGE:      // command %MIA0B0*%, %MIA0B1*%, %MIA1B0*%, %MIA1B1*% | |
|         m_MirrorA = m_MirrorB = 0; | |
|         while( *text && *text != '*' ) | |
|         { | |
|             switch( *text ) | |
|             { | |
|             case 'A':       // Mirror A axis ? | |
|                 text++; | |
|                 if( *text == '1' ) | |
|                     m_MirrorA = true; | |
|                 break; | |
| 
 | |
|             case 'B':       // Mirror B axis ? | |
|                 text++; | |
|                 if( *text == '1' ) | |
|                     m_MirrorB = true; | |
|                 break; | |
| 
 | |
|             default: | |
|                 text++; | |
|                 break; | |
|             } | |
|         } | |
|         break; | |
| 
 | |
|     case MODE_OF_UNITS: | |
|         code = ReadXCommand( text ); | |
|         if( code == INCH ) | |
|             m_GerbMetric = false; | |
|         else if( code == MILLIMETER ) | |
|             m_GerbMetric = true; | |
|         conv_scale = m_GerbMetric ? IU_PER_MILS / 25.4 : IU_PER_MILS; | |
|         break; | |
| 
 | |
|     case FILE_ATTRIBUTE:    // Command %TF ... | |
|         m_IsX2_file = true; | |
|     { | |
|         X2_ATTRIBUTE dummy; | |
|         dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text ); | |
| 
 | |
|         if( dummy.IsFileFunction() ) | |
|         { | |
|             delete m_FileFunction; | |
|             m_FileFunction = new X2_ATTRIBUTE_FILEFUNCTION( dummy ); | |
|         } | |
|         else if( dummy.IsFileMD5() ) | |
|         { | |
|             m_MD5_value = dummy.GetPrm( 1 ); | |
|         } | |
|         else if( dummy.IsFilePart() ) | |
|         { | |
|             m_PartString = dummy.GetPrm( 1 ); | |
|         } | |
|      } | |
|         break; | |
| 
 | |
|     case APERTURE_ATTRIBUTE:    // Command %TA ... Not yet supported | |
|         { | |
|         X2_ATTRIBUTE dummy; | |
|         dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text ); | |
| 
 | |
|         if( dummy.GetAttribute() == ".AperFunction" ) | |
|         { | |
|             m_AperFunction = dummy.GetPrm( 1 ); | |
| 
 | |
|             // A few function values can have other parameters. Add them | |
|             for( int ii = 2; ii < dummy.GetPrmCount(); ii++ ) | |
|                 m_AperFunction << "," << dummy.GetPrm( ii ); | |
|         } | |
|         } | |
|         break; | |
| 
 | |
|     case NET_ATTRIBUTE:    // Command %TO currently %TO.P %TO.N and %TO.C | |
|         { | |
|         X2_ATTRIBUTE dummy; | |
| 
 | |
|         dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text ); | |
| 
 | |
|         if( dummy.GetAttribute() == ".N" ) | |
|         { | |
|             m_NetAttributeDict.m_NetAttribType |= GBR_NETLIST_METADATA::GBR_NETINFO_NET; | |
|             m_NetAttributeDict.m_Netname = fromGerberString( dummy.GetPrm( 1 ) ); | |
|         } | |
|         else if( dummy.GetAttribute() == ".C" ) | |
|         { | |
|             m_NetAttributeDict.m_NetAttribType |= GBR_NETLIST_METADATA::GBR_NETINFO_CMP; | |
|             m_NetAttributeDict.m_Cmpref = fromGerberString( dummy.GetPrm( 1 ) ); | |
|         } | |
|         else if( dummy.GetAttribute() == ".P" ) | |
|         { | |
|             m_NetAttributeDict.m_NetAttribType |= GBR_NETLIST_METADATA::GBR_NETINFO_PAD; | |
|             m_NetAttributeDict.m_Cmpref = fromGerberString( dummy.GetPrm( 1 ) ); | |
|             m_NetAttributeDict.m_Padname = fromGerberString( dummy.GetPrm( 2 ) ); | |
|         } | |
|         } | |
|         break; | |
| 
 | |
|     case REMOVE_APERTURE_ATTRIBUTE:    // Command %TD ... | |
|         { | |
|         X2_ATTRIBUTE dummy; | |
|         dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text ); | |
|         RemoveAttribute( dummy ); | |
|         } | |
|         break; | |
| 
 | |
|     case OFFSET:        // command: OFAnnBnn (nn = float number) = layer Offset | |
|         m_Offset.x = m_Offset.y = 0; | |
|         while( *text != '*' ) | |
|         { | |
|             switch( *text ) | |
|             { | |
|             case 'A':       // A axis offset in current unit (inch or mm) | |
|                 text++; | |
|                 fcoord     = ReadDouble( text ); | |
|                 m_Offset.x = KiROUND( fcoord * conv_scale ); | |
|                 break; | |
| 
 | |
|             case 'B':       // B axis offset in current unit (inch or mm) | |
|                 text++; | |
|                 fcoord     = ReadDouble( text ); | |
|                 m_Offset.y = KiROUND( fcoord * conv_scale ); | |
|                 break; | |
|             } | |
|         } | |
|         break; | |
| 
 | |
|     case SCALE_FACTOR: | |
|         m_Scale.x = m_Scale.y = 1.0; | |
|         while( *text != '*' ) | |
|         { | |
|             switch( *text ) | |
|             { | |
|             case 'A':       // A axis scale | |
|                 text++; | |
|                 m_Scale.x = ReadDouble( text ); | |
|                 break; | |
| 
 | |
|             case 'B':       // B axis scale | |
|                 text++; | |
|                 m_Scale.y = ReadDouble( text ); | |
|                 break; | |
|             } | |
|         } | |
|         break; | |
| 
 | |
|     case IMAGE_OFFSET:  // command: IOAnnBnn (nn = float number) = Image Offset | |
|         m_ImageOffset.x = m_ImageOffset.y = 0; | |
|         while( *text != '*' ) | |
|         { | |
|             switch( *text ) | |
|             { | |
|             case 'A':       // A axis offset in current unit (inch or mm) | |
|                 text++; | |
|                 fcoord     = ReadDouble( text ); | |
|                 m_ImageOffset.x = KiROUND( fcoord * conv_scale ); | |
|                 break; | |
| 
 | |
|             case 'B':       // B axis offset in current unit (inch or mm) | |
|                 text++; | |
|                 fcoord     = ReadDouble( text ); | |
|                 m_ImageOffset.y = KiROUND( fcoord * conv_scale ); | |
|                 break; | |
|             } | |
|         } | |
|         break; | |
| 
 | |
|     case IMAGE_ROTATION:    // command IR0* or IR90* or IR180* or IR270* | |
|         if( strncasecmp( text, "0*", 2 ) == 0 ) | |
|             m_ImageRotation = 0; | |
|         else if( strncasecmp( text, "90*", 3 ) == 0 ) | |
|             m_ImageRotation = 90; | |
|         else if( strncasecmp( text, "180*", 4 ) == 0 ) | |
|             m_ImageRotation = 180; | |
|         else if( strncasecmp( text, "270*", 4 ) == 0 ) | |
|             m_ImageRotation = 270; | |
|         else | |
|             AddMessageToList( _( "RS274X: Command \"IR\" rotation value not allowed" ) ); | |
|         break; | |
| 
 | |
|     case STEP_AND_REPEAT:   // command SR, like %SRX3Y2I5.0J2*% | |
|         m_Iterpolation = GERB_INTERPOL_LINEAR_1X;       // Start a new Gerber layer | |
|         GetLayerParams().m_StepForRepeat.x = 0.0; | |
|         GetLayerParams().m_StepForRepeat.x = 0.0;       // offset for Step and Repeat command | |
|         GetLayerParams().m_XRepeatCount = 1; | |
|         GetLayerParams().m_YRepeatCount = 1;            // The repeat count | |
|         GetLayerParams().m_StepForRepeatMetric = m_GerbMetric;  // the step units | |
|         while( *text && *text != '*' ) | |
|         { | |
|             switch( *text ) | |
|             { | |
|             case 'I':       // X axis offset | |
|                 text++; | |
|                 GetLayerParams().m_StepForRepeat.x = ReadDouble( text ); | |
|                 break; | |
| 
 | |
|             case 'J':       // Y axis offset | |
|                 text++; | |
|                 GetLayerParams().m_StepForRepeat.y = ReadDouble( text ); | |
|                 break; | |
| 
 | |
|             case 'X':       // X axis repeat count | |
|                 text++; | |
|                 GetLayerParams().m_XRepeatCount = ReadInt( text ); | |
|                 break; | |
| 
 | |
|             case 'Y':       // Y axis offset | |
|                 text++; | |
|                 GetLayerParams().m_YRepeatCount = ReadInt( text ); | |
|                 break; | |
|             default: | |
|                 text++; | |
|                 break; | |
|             } | |
|         } | |
|         break; | |
| 
 | |
|     case IMAGE_JUSTIFY: // Command IJAnBn* | |
|         m_ImageJustifyXCenter = false;          // Image Justify Center on X axis (default = false) | |
|         m_ImageJustifyYCenter = false;          // Image Justify Center on Y axis (default = false) | |
|         m_ImageJustifyOffset = wxPoint(0,0);    // Image Justify Offset on XY axis (default = 0,0) | |
|         while( *text && *text != '*' ) | |
|         { | |
|             // IJ command is (for A or B axis) AC or AL or A<coordinate> | |
|             switch( *text ) | |
|             { | |
|             case 'A':       // A axis justify | |
|                 text++; | |
|                 if( *text == 'C' ) | |
|                 { | |
|                     m_ImageJustifyXCenter = true; | |
|                     text++; | |
|                 } | |
|                 else if( *text == 'L' ) | |
|                 { | |
|                     m_ImageJustifyXCenter = true; | |
|                     text++; | |
|                 } | |
|                 else m_ImageJustifyOffset.x = KiROUND( ReadDouble( text ) * conv_scale); | |
|                 break; | |
| 
 | |
|             case 'B':       // B axis justify | |
|                 text++; | |
|                 if( *text == 'C' ) | |
|                 { | |
|                     m_ImageJustifyYCenter = true; | |
|                     text++; | |
|                 } | |
|                 else if( *text == 'L' ) | |
|                 { | |
|                     m_ImageJustifyYCenter = true; | |
|                     text++; | |
|                 } | |
|                 else m_ImageJustifyOffset.y = KiROUND( ReadDouble( text ) * conv_scale); | |
|                 break; | |
|             default: | |
|                 text++; | |
|                 break; | |
|             } | |
|         } | |
|         if( m_ImageJustifyXCenter ) | |
|             m_ImageJustifyOffset.x = 0; | |
|         if( m_ImageJustifyYCenter ) | |
|             m_ImageJustifyOffset.y = 0; | |
|         break; | |
| 
 | |
|     case KNOCKOUT: | |
|         m_Iterpolation = GERB_INTERPOL_LINEAR_1X;       // Start a new Gerber layer | |
|         msg = _( "RS274X: Command KNOCKOUT ignored by GerbView" ) ; | |
|         AddMessageToList( msg ); | |
|         break; | |
| 
 | |
|     case PLOTTER_FILM:  // Command PF <string> | |
|         // This is an info about film that must be used to plot this file | |
|         // Has no meaning here. We just display this string | |
|         msg = wxT( "Plotter Film info:<br>" ); | |
|         while( *text != '*' ) | |
|         { | |
|            msg.Append( *text++ ); | |
|         } | |
|         AddMessageToList( msg ); | |
|         break; | |
| 
 | |
|     case ROTATE:        // Layer rotation: command like %RO45*% | |
|         m_Iterpolation = GERB_INTERPOL_LINEAR_1X;       // Start a new Gerber layer | |
|         m_LocalRotation =ReadDouble( text );             // Store layer rotation in degrees | |
|         break; | |
| 
 | |
|     case IMAGE_NAME: | |
|         m_ImageName.Empty(); | |
|         while( *text != '*' ) | |
|         { | |
|             m_ImageName.Append( *text++ ); | |
|         } | |
| 
 | |
|         break; | |
| 
 | |
|     case LAYER_NAME: | |
|         m_Iterpolation = GERB_INTERPOL_LINEAR_1X;       // Start a new Gerber layer | |
|         GetLayerParams( ).m_LayerName.Empty(); | |
|         while( *text != '*' ) | |
|         { | |
|             GetLayerParams( ).m_LayerName.Append( *text++ ); | |
|         } | |
| 
 | |
|         break; | |
| 
 | |
|     case IMAGE_POLARITY: | |
|         if( strncasecmp( text, "NEG", 3 ) == 0 ) | |
|             m_ImageNegative = true; | |
|         else | |
|             m_ImageNegative = false; | |
|         DBG( printf( "%22s: IMAGE_POLARITY m_ImageNegative=%s\n", __func__, | |
|                    m_ImageNegative ? "true" : "false" ); ) | |
|         break; | |
| 
 | |
|     case LAYER_POLARITY: | |
|         if( *text == 'C' ) | |
|             GetLayerParams().m_LayerNegative = true; | |
| 
 | |
|         else | |
|             GetLayerParams().m_LayerNegative = false; | |
| 
 | |
| //        DBG( printf( "%22s: LAYER_POLARITY m_LayerNegative=%s\n", __func__, | |
| //                   GetLayerParams().m_LayerNegative ? "true" : "false" ); ) | |
|         break; | |
| 
 | |
|     case INCLUDE_FILE: | |
|         if( m_FilesPtr >= INCLUDE_FILES_CNT_MAX ) | |
|         { | |
|             ok = false; | |
|             AddMessageToList( _( "Too many include files!!" ) ); | |
|             break; | |
|         } | |
| 
 | |
|         strncpy( line, text, sizeof(line)-1 ); | |
|         line[sizeof(line)-1] = '\0'; | |
| 
 | |
|         strtok( line, "*%%\n\r" ); | |
|         m_FilesList[m_FilesPtr] = m_Current_File; | |
| 
 | |
|         m_Current_File = fopen( line, "rt" ); | |
|         if( m_Current_File == 0 ) | |
|         { | |
|             msg.Printf( wxT( "include file <%s> not found." ), line ); | |
|             AddMessageToList( msg ); | |
|             ok = false; | |
|             m_Current_File = m_FilesList[m_FilesPtr]; | |
|             break; | |
|         } | |
|         m_FilesPtr++; | |
|         break; | |
| 
 | |
|     case AP_MACRO:  // lines like %AMMYMACRO* | |
|                     // 5,1,8,0,0,1.08239X$1,22.5* | |
|                     // % | |
|         /*ok = */ReadApertureMacro( buff, text, m_Current_File ); | |
|         break; | |
| 
 | |
|     case AP_DEFINITION: | |
|         /* input example:  %ADD30R,0.081800X0.101500*% | |
|          * Aperture definition has 4 options: C, R, O, P | |
|          * (Circle, Rect, Oval, regular Polygon) | |
|          * and shapes can have a hole (round or rectangular). | |
|          * All optional parameters values start by X | |
|          * at this point, text points to 2nd 'D' | |
|          */ | |
|         if( *text++ != 'D' ) | |
|         { | |
|             ok = false; | |
|             break; | |
|         } | |
| 
 | |
|         m_Has_DCode = true; | |
| 
 | |
|         code = ReadInt( text ); | |
| 
 | |
|         D_CODE* dcode; | |
|         dcode = GetDCODE( code ); | |
| 
 | |
|         if( dcode == NULL ) | |
|             break; | |
| 
 | |
|         dcode->m_AperFunction = m_AperFunction; | |
| 
 | |
|         // at this point, text points to character after the ADD<num>, | |
|         // i.e. R in example above.  If text[0] is one of the usual | |
|         // apertures: (C,R,O,P), there is a comma after it. | |
|         if( text[1] == ',' ) | |
|         { | |
|             char stdAperture = *text; | |
| 
 | |
|             text += 2;              // skip "C," for example | |
|  | |
|             dcode->m_Size.x = KiROUND( ReadDouble( text ) * conv_scale ); | |
|             dcode->m_Size.y = dcode->m_Size.x; | |
| 
 | |
|             switch( stdAperture )   // Aperture desceiption has optional parameters. Read them | |
|             { | |
|             case 'C':               // Circle | |
|                 dcode->m_Shape = APT_CIRCLE; | |
|                 while( *text == ' ' ) | |
|                     text++; | |
| 
 | |
|                 if( *text == 'X' ) | |
|                 { | |
|                     text++; | |
|                     dcode->m_Drill.x = dcode->m_Drill.y = | |
|                                            KiROUND( ReadDouble( text ) * conv_scale ); | |
|                     dcode->m_DrillShape = APT_DEF_ROUND_HOLE; | |
|                 } | |
| 
 | |
|                 while( *text == ' ' ) | |
|                     text++; | |
| 
 | |
|                 if( *text == 'X' ) | |
|                 { | |
|                     text++; | |
|                     dcode->m_Drill.y = | |
|                         KiROUND( ReadDouble( text ) * conv_scale ); | |
| 
 | |
|                     dcode->m_DrillShape = APT_DEF_RECT_HOLE; | |
|                 } | |
|                 dcode->m_Defined = true; | |
|                 break; | |
| 
 | |
|             case 'O':               // oval | |
|             case 'R':               // rect | |
|                 dcode->m_Shape = (stdAperture == 'O') ? APT_OVAL : APT_RECT; | |
| 
 | |
|                 while( *text == ' ' ) | |
|                     text++; | |
| 
 | |
|                 if( *text == 'X' ) | |
|                 { | |
|                     text++; | |
|                     dcode->m_Size.y = | |
|                         KiROUND( ReadDouble( text ) * conv_scale ); | |
|                 } | |
| 
 | |
|                 while( *text == ' ' ) | |
|                     text++; | |
| 
 | |
|                 if( *text == 'X' ) | |
|                 { | |
|                     text++; | |
|                     dcode->m_Drill.x = KiROUND( ReadDouble( text ) * conv_scale ); | |
|                     dcode->m_Drill.y = dcode->m_Drill.x; | |
|                     dcode->m_DrillShape = APT_DEF_ROUND_HOLE; | |
|                 } | |
| 
 | |
|                 while( *text == ' ' ) | |
|                     text++; | |
| 
 | |
|                 if( *text == 'X' ) | |
|                 { | |
|                     text++; | |
|                     dcode->m_Drill.y = | |
|                         KiROUND( ReadDouble( text ) * conv_scale ); | |
|                     dcode->m_DrillShape = APT_DEF_RECT_HOLE; | |
|                 } | |
|                 dcode->m_Defined = true; | |
|                 break; | |
| 
 | |
|             case 'P': | |
| 
 | |
|                 /* Regular polygon: a command line like %ADD12P,0.040X10X25X0.025X0.025X0.0150*% | |
|                  * params are: <diameter>, X<edge count>, X<Rotation>, X<X hole dim>, X<Y hole dim> | |
|                  */ | |
|                 dcode->m_Shape = APT_POLYGON; | |
|                 while( *text == ' ' ) | |
|                     text++; | |
| 
 | |
|                 if( *text == 'X' ) | |
|                 { | |
|                     text++; | |
|                     dcode->m_EdgesCount = ReadInt( text ); | |
|                 } | |
| 
 | |
|                 while( *text == ' ' ) | |
|                     text++; | |
| 
 | |
|                 if( *text == 'X' ) | |
|                 { | |
|                     text++; | |
|                     dcode->m_Rotation = ReadDouble( text ); | |
|                 } | |
| 
 | |
|                 while( *text == ' ' ) | |
|                     text++; | |
| 
 | |
|                 if( *text == 'X' ) | |
|                 { | |
|                     text++; | |
|                     dcode->m_Drill.x = KiROUND( ReadDouble( text ) * conv_scale ); | |
|                     dcode->m_Drill.y = dcode->m_Drill.x = | |
|                     dcode->m_DrillShape = APT_DEF_ROUND_HOLE; | |
|                 } | |
| 
 | |
|                 while( *text == ' ' ) | |
|                     text++; | |
| 
 | |
|                 if( *text == 'X' ) | |
|                 { | |
|                     text++; | |
|                     dcode->m_Drill.y = KiROUND( ReadDouble( text ) * conv_scale ); | |
|                     dcode->m_DrillShape = APT_DEF_RECT_HOLE; | |
|                 } | |
|                 dcode->m_Defined = true; | |
|                 break; | |
|             } | |
|         } | |
|         else    // text[0] starts an aperture macro name | |
|         { | |
|             APERTURE_MACRO am_lookup; | |
| 
 | |
|             while( *text && *text != '*' && *text != ',' ) | |
|                 am_lookup.name.Append( *text++ ); | |
| 
 | |
|             // When an aperture definition is like %AMLINE2* 22,1,$1,$2,0,0,-45* | |
|             // the ADDxx<MACRO_NAME> command has parameters, like %ADD14LINE2,0.8X0.5*% | |
|             if( *text == ',' ) | |
|             {   // Read aperture macro parameters and store them | |
|                 text++;     // text points the first parameter | |
|  | |
|                 while( *text && *text != '*' ) | |
|                 { | |
|                     double param = ReadDouble( text ); | |
|                     dcode->AppendParam( param ); | |
| 
 | |
|                     while( isspace( *text ) ) | |
|                         text++; | |
| 
 | |
|                     // Skip 'X' separator: | |
|                     if( *text == 'X' || *text == 'x' ) | |
|                         text++; | |
|                 } | |
|             } | |
| 
 | |
|             // lookup the aperture macro here. | |
|             APERTURE_MACRO* pam = FindApertureMacro( am_lookup ); | |
| 
 | |
|             if( !pam ) | |
|             { | |
|                 msg.Printf( wxT( "RS274X: aperture macro %s not found\n" ), | |
|                            TO_UTF8( am_lookup.name ) ); | |
|                 AddMessageToList( msg ); | |
|                 ok = false; | |
|                 break; | |
|             } | |
| 
 | |
|             dcode->m_Shape = APT_MACRO; | |
|             dcode->SetMacro( pam ); | |
|         } | |
| 
 | |
|         break; | |
| 
 | |
|     default: | |
|         ok = false; | |
|         break; | |
|     } | |
| 
 | |
|     (void) seq_len;     // quiet g++, or delete the unused variable. | |
|  | |
|     ok = GetEndOfBlock( buff, text, m_Current_File ); | |
| 
 | |
|     return ok; | |
| } | |
| 
 | |
| 
 | |
| bool GetEndOfBlock( char* buff, char*& text, FILE* gerber_file ) | |
| { | |
|     for( ; ; ) | |
|     { | |
|         while( (text < buff + GERBER_BUFZ) && *text ) | |
|         { | |
|             if( *text == '*' ) | |
|                 return true; | |
| 
 | |
|             if( *text == '%' ) | |
|                 return true; | |
| 
 | |
|             text++; | |
|         } | |
| 
 | |
|         if( fgets( buff, GERBER_BUFZ, gerber_file ) == NULL ) | |
|             break; | |
| 
 | |
|         text = buff; | |
|     } | |
| 
 | |
|     return false; | |
| } | |
| 
 | |
| 
 | |
| /** | |
|  * Function GetNextLine | |
|  * test for an end of line | |
|  * if an end of line is found: | |
|  *   read a new line | |
|  * @param aBuff = buffer (size = GERBER_BUFZ) to fill with a new line | |
|  * @param aText = pointer to the last useful char in aBuff | |
|  *          on return: points the beginning of the next line. | |
|  * @param aFile = the opened GERBER file to read | |
|  * @return a pointer to the beginning of the next line or NULL if end of file | |
| */ | |
| static char* GetNextLine(  char *aBuff, char* aText, FILE* aFile  ) | |
| { | |
|     for( ; ; ) | |
|     { | |
|         switch (*aText ) | |
|         { | |
|             case ' ':     // skip blanks | |
|             case '\n': | |
|             case '\r':    // Skip line terminators | |
|                 ++aText; | |
|                 break; | |
| 
 | |
|             case 0:    // End of text found in aBuff: Read a new string | |
|                 if( fgets( aBuff, GERBER_BUFZ, aFile ) == NULL ) | |
|                     return NULL; | |
|                 aText = aBuff; | |
|                 return aText; | |
| 
 | |
|             default: | |
|                 return aText; | |
|         } | |
|     } | |
|     return aText; | |
| } | |
| 
 | |
| 
 | |
| bool GERBER_FILE_IMAGE::ReadApertureMacro( char *buff, | |
|                                 char*&    text, | |
|                                 FILE*     gerber_file ) | |
| { | |
|     wxString       msg; | |
|     APERTURE_MACRO am; | |
| 
 | |
|     // read macro name | |
|     while( *text ) | |
|     { | |
|         if( *text == '*' ) | |
|         { | |
|             ++text; | |
|             break; | |
|         } | |
| 
 | |
|         am.name.Append( *text++ ); | |
|     } | |
| 
 | |
|     // Read aperture macro parameters | |
|     for( ; ; ) | |
|     { | |
|         if( *text == '*' ) | |
|             ++text; | |
| 
 | |
|         text = GetNextLine( buff, text, gerber_file ); | |
| 
 | |
|         if( text == NULL )  // End of File | |
|             return false; | |
| 
 | |
|         // text points the beginning of a new line. | |
|  | |
|         // Test for the last line in aperture macro lis: | |
|         // last line is % or *% sometime found. | |
|         if( *text == '*' ) | |
|             ++text; | |
| 
 | |
|         if( *text == '%' ) | |
|             break;      // exit with text still pointing at % | |
|  | |
|         int paramCount = 0; // will be set to the minimal parameters count, | |
|                             // depending on the actual primitive | |
|         int primitive_type = AMP_UNKNOWN; | |
|         // Test for a valid symbol at the beginning of a description: | |
|         // it can be: a parameter declaration like $1=$2/4 | |
|         // or a digit (macro primitive selection) | |
|         // all other symbols are illegal. | |
|         if( *text == '$' )  // local parameter declaration, inside the aperture macro | |
|         { | |
|             am.m_localparamStack.push_back( AM_PARAM() ); | |
|             AM_PARAM& param = am.m_localparamStack.back(); | |
|             text = GetNextLine(  buff, text, gerber_file ); | |
|             if( text == NULL)   // End of File | |
|                 return false; | |
|             param.ReadParam( text ); | |
|             continue; | |
|         } | |
|         else if( !isdigit(*text)  )     // Ill. symbol | |
|         { | |
|             msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": ill. symbol, line: \"%s\"" ), | |
|                         GetChars( am.name ), GetChars( FROM_UTF8( buff ) ) ); | |
|             AddMessageToList( msg ); | |
|             primitive_type = AMP_COMMENT; | |
|         } | |
|         else | |
|             primitive_type = ReadInt( text ); | |
| 
 | |
|         bool is_comment = false; | |
| 
 | |
|         switch( primitive_type ) | |
|         { | |
|         case AMP_COMMENT:     // lines starting by 0 are a comment | |
|             paramCount = 0; | |
|             is_comment = true; | |
|             // Skip comment | |
|             while( *text && ( *text != '*' ) ) | |
|                 text++; | |
|             break; | |
| 
 | |
|         case AMP_CIRCLE: | |
|             paramCount = 4; // minimal count. can have a optional parameter (rotation) | |
|             break; | |
| 
 | |
|         case AMP_LINE2: | |
|         case AMP_LINE20: | |
|             paramCount = 7; | |
|             break; | |
| 
 | |
|         case AMP_LINE_CENTER: | |
|         case AMP_LINE_LOWER_LEFT: | |
|             paramCount = 6; | |
|             break; | |
| 
 | |
|         case AMP_EOF: | |
|             paramCount = 0; | |
|             break; | |
| 
 | |
|         case AMP_OUTLINE: | |
|             paramCount = 4; | |
|             break; | |
| 
 | |
|         case AMP_POLYGON: | |
|             paramCount = 6; | |
|             break; | |
| 
 | |
|         case AMP_MOIRE: | |
|             paramCount = 9; | |
|             break; | |
| 
 | |
|         case AMP_THERMAL: | |
|             paramCount = 6; | |
|             break; | |
| 
 | |
|         default: | |
|             // @todo, there needs to be a way of reporting the line number | |
|             msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Invalid primitive id code %d, line: \"%s\"" ), | |
|                         GetChars( am.name ), primitive_type,  GetChars( FROM_UTF8( buff ) ) ); | |
|             AddMessageToList( msg ); | |
|             return false; | |
|         } | |
| 
 | |
|         if( is_comment ) | |
|             continue; | |
| 
 | |
|         AM_PRIMITIVE prim( m_GerbMetric ); | |
|         prim.primitive_id = (AM_PRIMITIVE_ID) primitive_type; | |
|         int ii; | |
| 
 | |
|         for( ii = 0; ii < *text && *text != '*'; ++ii ) | |
|         { | |
|             prim.params.push_back( AM_PARAM() ); | |
| 
 | |
|             AM_PARAM& param = prim.params.back(); | |
| 
 | |
|             text = GetNextLine(  buff, text, gerber_file ); | |
| 
 | |
|             if( text == NULL)   // End of File | |
|                 return false; | |
| 
 | |
|             param.ReadParam( text ); | |
|         } | |
| 
 | |
|         if( ii < paramCount ) | |
|         { | |
|             // maybe some day we can throw an exception and track a line number | |
|             msg.Printf( wxT( "RS274X: read macro descr type %d: read %d parameters, insufficient parameters\n" ), | |
|                         prim.primitive_id, ii ); | |
|             AddMessageToList( msg ); | |
| 
 | |
|         } | |
|         // there are more parameters to read if this is an AMP_OUTLINE | |
|         if( prim.primitive_id == AMP_OUTLINE ) | |
|         { | |
|             // so far we have read [0]:exposure, [1]:#points, [2]:X start, [3]: Y start | |
|             // Now read all the points, plus trailing rotation in degrees. | |
|  | |
|             // params[1] is a count of polygon points, so it must be given | |
|             // in advance, i.e. be immediate. | |
|             wxASSERT( prim.params[1].IsImmediate() ); | |
| 
 | |
|             paramCount = (int) prim.params[1].GetValue( 0 ) * 2 + 1; | |
| 
 | |
|             for( int jj = 0; jj < paramCount && *text != '*'; ++jj ) | |
|             { | |
|                 prim.params.push_back( AM_PARAM() ); | |
| 
 | |
|                 AM_PARAM& param = prim.params.back(); | |
| 
 | |
|                 text = GetNextLine(  buff, text, gerber_file ); | |
| 
 | |
|                 if( text == NULL )  // End of File | |
|                     return false; | |
| 
 | |
|                 param.ReadParam( text ); | |
|             } | |
|         } | |
| 
 | |
|         am.primitives.push_back( prim ); | |
|     } | |
| 
 | |
|     m_aperture_macros.insert( am ); | |
| 
 | |
|     return true; | |
| } | |
| 
 |