void GERBER_FILE_IMAGE::RemoveAttribute( X2_ATTRIBUTE& aAttribute )
{
    /* Called when a %TD command is found
     * Remove the attribute specified by the %TD command.
     * is no attribute, all current attributes specified by the %TO and the %TA
     * commands are cleared.
     * if a attribute name is specified (for instance %TD.CN*%) is specified,
     * only this attribute is cleared
     */
    m_NetAttributeDict.ClearAttribute( &aAttribute.GetPrm( 1 ) );

    if( aAttribute.GetPrm( 1 ).IsEmpty() || aAttribute.GetPrm( 1 ) == ".AperFunction" )
        m_AperFunction.Clear();
}
/*
 * class X2_ATTRIBUTE_FILEFUNCTION ( from %TF.FileFunction in Gerber file)
 *  Example file function:
 *  %TF.FileFunction,Copper,L1,Top*%
 * - Type. Such as copper, solder mask etc.
 * - Position. Specifies where the file appears in the PCB layer structure.
 *      Corresponding position substring:
 *      Copper layer:   L1, L2, L3...to indicate the layer position followed by Top, Inr or
 *                      Bot. L1 is always the top copper layer. E.g. L2,Inr.
 *      Extra layer, e.g. solder mask: Top or Bot – defines the attachment of the layer.
 *      Drill/rout layer: E.g. 1,4 – where 1 is the start and 4 is the end copper layer. The
 *                        pair 1,4 defines the span of the drill/rout file
 * Optional index. This can be used in instances where for example there are two solder
 *                 masks on the same side. The index counts from the PCB surface outwards.
 */
X2_ATTRIBUTE_FILEFUNCTION::X2_ATTRIBUTE_FILEFUNCTION( X2_ATTRIBUTE& aAttributeBase )
    : X2_ATTRIBUTE()
{
    m_Prms = aAttributeBase.GetPrms();
    m_z_order = 0;

    //ensure at least 5 parameters
    while( GetPrmCount() < 5 )
        m_Prms.Add( wxEmptyString );

    set_Z_Order();
}
bool EXCELLON_IMAGE::LoadFile( const wxString & aFullFileName )
{
    // Set the default parmeter values:
    ResetDefaultValues();
    ClearMessageList();

    m_Current_File = wxFopen( aFullFileName, wxT( "rt" ) );

    if( m_Current_File == NULL )
        return false;

    m_FileName = aFullFileName;

    LOCALE_IO toggleIo;

    // FILE_LINE_READER will close the file.
    FILE_LINE_READER excellonReader( m_Current_File, m_FileName );

    while( true )
    {
        if( excellonReader.ReadLine() == 0 )
            break;

        char* line = excellonReader.Line();
        char* text = StrPurge( line );

        if( *text == ';' )       // comment: skip line
            continue;

        if( m_State == EXCELLON_IMAGE::READ_HEADER_STATE )
        {
            Execute_HEADER_Command( text );
        }
        else
        {
            switch( *text )
            {
            case 'M':
                Execute_HEADER_Command( text );
                break;

            case 'G': /* Line type Gxx : command */
                Execute_EXCELLON_G_Command( text );
                break;

            case 'X':
            case 'Y':               // command like X12550Y19250
                Execute_Drill_Command(text);
                break;

            case 'I':
            case 'J':               /* Auxiliary Move command */
                m_IJPos = ReadIJCoord( text );
                if( *text == '*' )  // command like X35142Y15945J504
                {
                    Execute_Drill_Command( text);
                }
                break;

            case 'T': // Tool command
                Select_Tool( text );
                break;

            case '%':
                break;

            default:
            {
                wxString msg;
                msg.Printf( wxT( "Unexpected symbol &lt;%c&gt;" ), *text );
                AddMessageToList( msg );
            }
                break;
            }   // End switch
        }
    }

    // Add our file attribute, to identify the drill file
    X2_ATTRIBUTE dummy;
    char* text = (char*)file_attribute;
    dummy.ParseAttribCmd( m_Current_File, NULL, 0, text );
    delete m_FileFunction;
    m_FileFunction = new X2_ATTRIBUTE_FILEFUNCTION( dummy );

    m_InUse = true;

    return true;
}
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;
        if( strnicmp( text, "90*", 2 ) == 0 )
            m_ImageRotation = 90;
        if( strnicmp( text, "180*", 2 ) == 0 )
            m_ImageRotation = 180;
        if( strnicmp( text, "270*", 2 ) == 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;