diff options
Diffstat (limited to 'gerbview/rs274x.cpp')
-rw-r--r-- | gerbview/rs274x.cpp | 1064 |
1 files changed, 1064 insertions, 0 deletions
diff --git a/gerbview/rs274x.cpp b/gerbview/rs274x.cpp new file mode 100644 index 0000000..6371404 --- /dev/null +++ b/gerbview/rs274x.cpp @@ -0,0 +1,1064 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2007-2014 Jean-Pierre Charras jp.charras at wanadoo.fr + * Copyright (C) 1992-2014 KiCad Developers, see change_log.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.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[GERBER_BUFZ], 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) + // TA (aperture attribute) and TD (delete aperture attribute) + FILE_ATTRIBUTE = CODE( 'T', 'F' ), + + // 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; + + if( text && *text ) + result = *text++ << 8; + else + return -1; + + if( text && *text ) + result += *text++; + else + return -1; + + return result; +} + + +bool GERBER_IMAGE::ReadRS274XCommand( char buff[GERBER_BUFZ], 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_IMAGE::ExecuteRS274XCommand( int command, + char buff[GERBER_BUFZ], + 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 ); + ReportMessage( msg ); + GetEndOfBlock( buff, text, m_Current_File ); + ok = false; + break; + } + } + if( !x_fmt_known || !y_fmt_known ) + ReportMessage( wxT( "RS274X: Format Statement (FS) without X or Y format" ) ); + + break; + + case AXIS_SELECT: // command ASAXBY*% or %ASAYBX*% + m_SwapAxis = false; + if( strnicmp( 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 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( strnicmp( text, "0*", 2 ) == 0 ) + m_ImageRotation = 0; + else if( strnicmp( text, "90*", 3 ) == 0 ) + m_ImageRotation = 90; + else if( strnicmp( text, "180*", 4 ) == 0 ) + m_ImageRotation = 180; + else if( strnicmp( text, "270*", 4 ) == 0 ) + m_ImageRotation = 270; + else + ReportMessage( _( "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" ) ; + ReportMessage( 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++ ); + } + ReportMessage( 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( strnicmp( 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; + ReportMessage( _( "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 ); + ReportMessage( 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; + + // 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++; + if( *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 ) ); + ReportMessage( msg ); + ok = false; + break; + } + + dcode->m_Shape = APT_MACRO; + dcode->SetMacro( (APERTURE_MACRO*) 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[GERBER_BUFZ], 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[GERBER_BUFZ], 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_IMAGE::ReadApertureMacro( char buff[GERBER_BUFZ], + 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 ); // Get next line + 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; + 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 ) ) ); + ReportMessage( msg ); + primitive_type = AMP_COMMENT; + } + else + primitive_type = ReadInt( text ); + + switch( primitive_type ) + { + case AMP_COMMENT: // lines starting by 0 are a comment + paramCount = 0; + // Skip comment + while( *text && (*text != '*') ) + text++; + break; + + case AMP_CIRCLE: + paramCount = 4; + 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 ) ) ); + ReportMessage( msg ); + return false; + } + + AM_PRIMITIVE prim( m_GerbMetric ); + prim.primitive_id = (AM_PRIMITIVE_ID) primitive_type; + int i; + + for( i = 0; i < paramCount && *text && *text != '*'; ++i ) + { + 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( i < 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, i ); + ReportMessage( 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 i = 0; i < paramCount && *text != '*'; ++i ) + { + 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; +} + |