Beispiel #1
0
        static GerberNet AddDrillHole(GerberImage image, DrillState drillState, GerberNet currentNet)
        {
            image.DrillStats.IncrementDrillCounter(drillState.CurrentTool);
            GerberNet newDrillNet = new GerberNet(image, currentNet, null, null);

            newDrillNet.BoundingBox = new BoundingBox();
            newDrillNet.StartX      = drillState.CurrentX;
            newDrillNet.StartY      = drillState.CurrentY;
            if (drillState.Unit == GerberUnit.Millimeter)
            {
                newDrillNet.StartX       /= 25.4;
                newDrillNet.StartY       /= 25.4;
                newDrillNet.NetState.Unit = GerberUnit.Inch;
            }

            newDrillNet.StopX         = newDrillNet.StartX - drillState.OriginX;
            newDrillNet.StopY         = newDrillNet.StartY - drillState.OriginY;
            newDrillNet.Aperture      = drillState.CurrentTool;
            newDrillNet.ApertureState = GerberApertureState.Flash;

            // Check if the aperture is set.
            if (image.ApertureArray[drillState.CurrentTool] == null)
            {
                return(newDrillNet);
            }

            newDrillNet.BoundingBox.Left   = newDrillNet.StartX - image.ApertureArray[drillState.CurrentTool].Parameters[0] / 2;
            newDrillNet.BoundingBox.Right  = newDrillNet.StartX + image.ApertureArray[drillState.CurrentTool].Parameters[0] / 2;
            newDrillNet.BoundingBox.Bottom = newDrillNet.StartY - image.ApertureArray[drillState.CurrentTool].Parameters[0] / 2;
            newDrillNet.BoundingBox.Top    = newDrillNet.StartY + image.ApertureArray[drillState.CurrentTool].Parameters[0] / 2;

            image.ImageInfo.MinX = Math.Min(image.ImageInfo.MinX, (newDrillNet.StartX - image.ApertureArray[drillState.CurrentTool].Parameters[0] / 2));
            image.ImageInfo.MinY = Math.Min(image.ImageInfo.MinY, (newDrillNet.StartY - image.ApertureArray[drillState.CurrentTool].Parameters[0] / 2));
            image.ImageInfo.MaxX = Math.Max(image.ImageInfo.MaxX, (newDrillNet.StartX + image.ApertureArray[drillState.CurrentTool].Parameters[0] / 2));
            image.ImageInfo.MaxY = Math.Max(image.ImageInfo.MaxY, (newDrillNet.StartY + image.ApertureArray[drillState.CurrentTool].Parameters[0] / 2));

            return(newDrillNet);
        }
Beispiel #2
0
        static void ParseCoordinate(GerberLineReader lineReader, char firstCharacter, GerberImage image, DrillState drillState)
        {
            if (drillState.CoordinateMode == DrillCoordinateMode.Absolute)
            {
                if (firstCharacter == 'X')
                {
                    drillState.CurrentX = GetDoubleValue(lineReader, drillState.DataNumberFormat, image.Format.OmitZeros, drillState.DecimalPlaces);
                    if (lineReader.Read() == 'Y')
                    {
                        drillState.CurrentY = GetDoubleValue(lineReader, drillState.DataNumberFormat, image.Format.OmitZeros, drillState.DecimalPlaces);
                    }

                    else
                    {
                        lineReader.Position--;
                    }
                }

                else
                {
                    drillState.CurrentY = GetDoubleValue(lineReader, drillState.DataNumberFormat, image.Format.OmitZeros, drillState.DecimalPlaces);
                }
            }

            else
            {
                if (firstCharacter == 'X')
                {
                    drillState.CurrentX += GetDoubleValue(lineReader, drillState.DataNumberFormat, image.Format.OmitZeros, drillState.DecimalPlaces);
                    if (lineReader.Read() == 'Y')
                    {
                        drillState.CurrentY += GetDoubleValue(lineReader, drillState.DataNumberFormat, image.Format.OmitZeros, drillState.DecimalPlaces);
                    }
                }

                else
                {
                    drillState.CurrentY += GetDoubleValue(lineReader, drillState.DataNumberFormat, image.Format.OmitZeros, drillState.DecimalPlaces);
                }
            }
        }
Beispiel #3
0
        static int ParseTCode(GerberLineReader lineReader, string drillFileName, DrillState drillState, GerberImage image)
        {
            int            drillNumber;
            double         drillSize = 0.0;
            int            length    = 0;
            char           nextCharacter;
            bool           done         = false;
            DrillFileStats stats        = image.DrillStats;
            string         line         = String.Empty;
            string         errorMessage = String.Empty;

            nextCharacter = lineReader.Read();
            if (!Char.IsDigit(nextCharacter))
            {
                if (nextCharacter == 'C')
                {
                    lineReader.Position -= 2;
                    line = lineReader.ReadLineToEnd();
                    if (line == "TCST")
                    {
                        errorMessage = "Tool change stop switch found.";
                        stats.AddNewError(-1, errorMessage, GerberErrorType.GerberNote, lineReader.LineNumber, drillFileName);
                    }

                    return(-1);
                }
            }

            lineReader.Position--;
            drillNumber = lineReader.GetIntegerValue(ref length);
            // T00 is a tool unload command.
            if (drillNumber == 0)
            {
                return(drillNumber);
            }

            if (drillNumber < ToolMin && drillNumber >= ToolMax)
            {
                errorMessage = String.Format(CultureInfo.CurrentCulture, "Drill number out of bounds:{0}.\n", drillNumber);
                stats.AddNewError(-1, errorMessage, GerberErrorType.GerberError, lineReader.LineNumber, drillFileName);
            }

            drillState.CurrentTool = drillNumber;

            // Tool definition following tool number.
            if (lineReader.Position > 0)
            {
                while (!done)
                {
                    nextCharacter = lineReader.Read();
                    switch (nextCharacter)
                    {
                    case 'C':
                        drillSize = GetDoubleValue(lineReader, drillState.HeaderNumberFormat, GerberOmitZero.OmitZerosTrailing, drillState.DecimalPlaces);
                        if (drillState.Unit == GerberUnit.Millimeter)
                        {
                            drillSize /= 25.4;
                        }

                        else if (drillSize >= 4.0)
                        {
                            drillSize /= 1000.0;
                        }

                        if (drillSize <= 0 || drillSize >= 10000)
                        {
                            errorMessage = "Unreasonable drill size found.";
                            stats.AddNewError(-1, errorMessage, GerberErrorType.GerberError, lineReader.LineNumber, drillFileName);
                        }

                        else
                        {
                            // Allow a redefinition of a tool only if all parameters are the same.
                            if (image.ApertureArray[drillNumber] != null)
                            {
                                if (image.ApertureArray[drillNumber].Parameters[0] != drillSize ||
                                    image.ApertureArray[drillNumber].ApertureType != GerberApertureType.Circle ||
                                    image.ApertureArray[drillNumber].ParameterCount != 1 ||
                                    image.ApertureArray[drillNumber].Unit != GerberUnit.Inch)
                                {
                                    errorMessage = String.Format(CultureInfo.CurrentCulture, "Found redefinition if drill {0}.\n", drillNumber);
                                    stats.AddNewError(-1, errorMessage, GerberErrorType.GerberError);
                                }
                            }

                            else
                            {
                                image.ApertureArray[drillNumber] = new ApertureDefinition();
                                image.ApertureArray[drillNumber].Parameters[0]  = drillSize;
                                image.ApertureArray[drillNumber].ApertureType   = GerberApertureType.Circle;
                                image.ApertureArray[drillNumber].ParameterCount = 1;
                                image.ApertureArray[drillNumber].Unit           = GerberUnit.Inch;
                            }
                        }

                        string drillUnit = (drillState.Unit == GerberUnit.Millimeter) ? "MM" : "INCH";
                        stats.AddToDrillList(drillNumber, (drillState.Unit == GerberUnit.Millimeter) ? drillSize * 25.4 : drillSize, drillUnit);
                        break;

                    case 'F':
                    case 'S':
                        lineReader.GetIntegerValue(ref length);
                        break;

                    default:
                        lineReader.Position--;
                        done = true;
                        break;
                    }
                }
            }

            return(drillNumber);
        }
Beispiel #4
0
        static DrillMCode ParseMCode(GerberLineReader lineReader, DrillState drillState, GerberImage image)
        {
            DrillFileStats stats  = image.DrillStats;
            DrillMCode     result = DrillMCode.Unknown;
            int            length = 0;
            string         line   = String.Empty;

            int value = lineReader.GetIntegerValue(ref length);

            switch (value)
            {
            case 0:
                stats.M00++;
                result = DrillMCode.End;
                break;

            case 1:
                stats.M01++;
                result = DrillMCode.EndPattern;
                break;

            case 18:
                stats.M18++;
                result = DrillMCode.TipCheck;
                break;

            case 25:
                stats.M25++;
                result = DrillMCode.BeginPattern;
                break;

            case 30:
                stats.M30++;
                result = DrillMCode.EndRewind;
                break;

            case 31:
                stats.M31++;
                result = DrillMCode.BeginPattern;
                break;

            case 45:
                stats.M45++;
                result = DrillMCode.LongMessage;
                break;

            case 47:
                stats.M47++;
                result = DrillMCode.Message;
                break;

            case 48:
                stats.M48++;
                result = DrillMCode.Header;
                break;

            case 71:
                stats.M71++;
                result = DrillMCode.Metric;
                break;

            case 72:
                stats.M72++;
                result = DrillMCode.Imperial;
                break;

            case 95:
                stats.M95++;
                result = DrillMCode.EndHeader;
                break;

            case 97:
                stats.M97++;
                result = DrillMCode.CannedText;
                break;

            case 98:
                stats.M98++;
                result = DrillMCode.CannedText;
                break;

            default:
                stats.MUnknown++;
                break;
            }

            return(result);
        }
Beispiel #5
0
        private static List <GerberHIDAttribute> drillAttributeList = new List <GerberHIDAttribute>(); /* <---- NOT SURE IF WE NEED THIS.*/

        public static GerberImage ParseDrillFile(string drillFileName, List <GerberHIDAttribute> attributeList, int numberOfAttributes, bool reload)
        {
            bool        foundEOF     = false;
            string      errorMessage = String.Empty;
            DrillState  drillState   = new DrillState();
            GerberImage image        = new GerberImage("Excellon Drill File");

            CreateDefaultAttributeList();
            if (reload & attributeList != null)
            {
                image.ImageInfo.NumberOfAttribute = numberOfAttributes;
                image.ImageInfo.AttributeList     = new List <GerberHIDAttribute>();
                for (int i = 0; i < numberOfAttributes; i++)
                {
                    GerberHIDAttribute attribute = new GerberHIDAttribute(attributeList[i]);
                    image.ImageInfo.AttributeList.Add(attribute);
                }
            }

            else
            {
                // Load default attributes.
                image.ImageInfo.NumberOfAttribute = drillAttributeList.Count;
                image.ImageInfo.AttributeList     = new List <GerberHIDAttribute>();
                for (int i = 0; i < image.ImageInfo.NumberOfAttribute; i++)
                {
                    GerberHIDAttribute attribute = new GerberHIDAttribute(drillAttributeList[i]);
                    image.ImageInfo.AttributeList.Add(attribute);
                }
            }

            DrillAttributeMerge(image.ImageInfo.AttributeList, image.ImageInfo.NumberOfAttribute, attributeList, numberOfAttributes);
            image.FileType         = GerberFileType.Drill;
            image.DrillStats       = new DrillFileStats();
            image.Format           = new GerberFormat();
            image.Format.OmitZeros = GerberOmitZero.OmitZerosUnspecified;

            if (image.ImageInfo.AttributeList[(int)HA.AutoDetect].DefaultValue.IntValue > 0)
            {
                drillState.AutoDetect       = false;
                drillState.DataNumberFormat = DrillNumberFormat.UserDefined;
                drillState.DecimalPlaces    = image.ImageInfo.AttributeList[(int)HA.Digits].DefaultValue.IntValue;
                if (image.ImageInfo.AttributeList[(int)HA.ZeroSuppression].DefaultValue.IntValue == (int)Units.Millimeters)
                {
                    drillState.Unit = GerberUnit.Millimeter;
                }

                switch (image.ImageInfo.AttributeList[(int)HA.ZeroSuppression].DefaultValue.IntValue)
                {
                case (int)ZeroSuppression.Leading:
                    image.Format.OmitZeros = GerberOmitZero.OmitZerosLeading;
                    break;

                case (int)ZeroSuppression.Trailing:
                    image.Format.OmitZeros = GerberOmitZero.OmitZerosTrailing;
                    break;

                default:
                    image.Format.OmitZeros = GerberOmitZero.OmitZerosExplicit;
                    break;
                }
            }

            //Debug.WriteLine(String.Format("Starting to parse drill file {0}", drillFileName));

            using (StreamReader drillFileStream = new StreamReader(drillFileName, Encoding.ASCII))
            {
                GerberLineReader lineReader = new GerberLineReader(drillFileStream);
                foundEOF = ParseDrillSegment(drillFileName, lineReader, image, drillState);
            }

            if (!foundEOF)
            {
                errorMessage = String.Format(CultureInfo.CurrentCulture, "File {0} is missing Excellon Drill EOF code./n", drillFileName);
                image.DrillStats.AddNewError(-1, errorMessage, GerberErrorType.GerberError);
            }

            //Debug.WriteLine("... done parsing Excellon Drill file.");
            return(image);
        }
Beispiel #6
0
        static bool ParseDrillSegment(string drillFileName, GerberLineReader lineReader, GerberImage image, DrillState drillState)
        {
            bool           foundEOF   = false;
            DrillFileStats stats      = image.DrillStats;
            GerberNet      currentNet = image.GerberNetList[0];

            currentNet.Level    = image.LevelList[0];
            currentNet.NetState = image.NetStateList[0];

            bool   done = false;
            string line;

            string[] command;
            string   errorMessage;
            char     nextCharacter;

            while (!lineReader.EndOfFile && !foundEOF)
            {
                nextCharacter = lineReader.Read();
                switch (nextCharacter)
                {
                case ';':       // Comment.
                    line = lineReader.ReadLineToEnd();
                    break;

                case 'D':
                    lineReader.Position--;
                    line = lineReader.ReadLineToEnd();
                    if (line.Substring(0, 6) == "DETECT")
                    {
                        stats.Detect = line.Substring(6, (line.Length - 7));
                    }

                    else
                    {
                        errorMessage = String.Format(CultureInfo.CurrentCulture, "Undefined header line: {0}.\n", line);
                        stats.AddNewError(-1, errorMessage, GerberErrorType.GerberNote, lineReader.LineNumber, drillFileName);
                    }
                    break;

                case 'F':
                    lineReader.Position--;
                    line = lineReader.ReadLineToEnd();
                    if (line != "FMAT,2")
                    {
                        errorMessage = String.Format(CultureInfo.CurrentCulture, "Undefined header line: {0}.\n", line);
                        stats.AddNewError(-1, errorMessage, GerberErrorType.GerberNote, lineReader.LineNumber, drillFileName);
                    }
                    break;

                case 'G':
                    DrillGCode gCode = ParseGCode(lineReader, image);
                    switch (gCode)
                    {
                    case DrillGCode.Rout:
                        errorMessage = "Rout Mode not supported.\n";
                        stats.AddNewError(-1, errorMessage, GerberErrorType.GerberNote, lineReader.LineNumber, drillFileName);
                        break;

                    case DrillGCode.Drill:
                        break;

                    case DrillGCode.Slot:
                        nextCharacter = lineReader.Read();
                        ParseCoordinate(lineReader, nextCharacter, image, drillState);
                        currentNet.StopX = drillState.CurrentX;
                        currentNet.StopY = drillState.CurrentY;
                        if (drillState.Unit == GerberUnit.Millimeter)
                        {
                            currentNet.StopX /= 25.4;
                            currentNet.StopY /= 25.4;
                        }

                        currentNet.ApertureState = GerberApertureState.On;
                        break;

                    case DrillGCode.Absolute:
                        drillState.CoordinateMode = DrillCoordinateMode.Absolute;
                        break;

                    case DrillGCode.Incrementle:
                        drillState.CoordinateMode = DrillCoordinateMode.Incremental;
                        break;

                    case DrillGCode.ZeroSet:
                        nextCharacter = lineReader.Read();
                        ParseCoordinate(lineReader, nextCharacter, image, drillState);
                        drillState.OriginX = drillState.CurrentX;
                        drillState.OriginY = drillState.CurrentY;
                        break;

                    default:
                        line = lineReader.ReadLineToEnd();
                        break;
                    }

                    break;

                case 'I':       // Inch header.
                    if (drillState.CurrentSection != DrillFileSection.Header)
                    {
                        break;
                    }

                    nextCharacter = lineReader.Read();
                    switch (nextCharacter)
                    {
                    // Inch
                    case 'N':
                        lineReader.Position -= 2;
                        line    = lineReader.ReadLineToEnd();
                        command = line.Split(',');
                        if (command[0] == "INCH")
                        {
                            drillState.Unit = GerberUnit.Inch;
                        }

                        if (command.Length == 2)
                        {
                            if (command[1] == "TZ")
                            {
                                if (drillState.AutoDetect)
                                {
                                    image.Format.OmitZeros        = GerberOmitZero.OmitZerosLeading;
                                    drillState.HeaderNumberFormat = DrillNumberFormat.Format_00_0000;
                                    drillState.DecimalPlaces      = 4;
                                }
                            }

                            else if (command[1] == "LZ")
                            {
                                image.Format.OmitZeros        = GerberOmitZero.OmitZerosTrailing;
                                drillState.HeaderNumberFormat = DrillNumberFormat.Format_00_0000;
                                drillState.DecimalPlaces      = 4;
                            }

                            else
                            {
                                errorMessage = "Invalid zero suppression found after INCH.\n";
                                stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning, lineReader.LineNumber, drillFileName);
                            }
                        }

                        else
                        {
                            // No TZ/LZ specified, use defaults.
                            if (drillState.AutoDetect)
                            {
                                image.Format.OmitZeros        = GerberOmitZero.OmitZerosLeading;
                                drillState.HeaderNumberFormat = DrillNumberFormat.Format_00_0000;
                                drillState.DecimalPlaces      = 4;
                            }
                        }

                        break;

                    case 'C':
                        lineReader.Position -= 2;
                        line    = lineReader.ReadLineToEnd();
                        command = line.Split(',');
                        if (command.Length == 2)
                        {
                            if (command[1] == "ON")
                            {
                                drillState.CoordinateMode = DrillCoordinateMode.Incremental;
                            }

                            else if (command[1] == "OFF")
                            {
                                drillState.CoordinateMode = DrillCoordinateMode.Absolute;
                            }

                            else
                            {
                                errorMessage = "Invalid coordinate data found.\n";
                                stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning, lineReader.LineNumber, drillFileName);
                            }
                        }

                        else
                        {
                            errorMessage = "Invalid data found.\n";
                            stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning, lineReader.LineNumber, drillFileName);
                        }

                        break;
                    }

                    break;

                case 'M':                             // M code or Metric
                    nextCharacter = lineReader.Read();
                    if (!Char.IsDigit(nextCharacter)) // Not a M## command.
                    {
                        // Should be a metric command in header.
                        // METRIC is only acceptable within the header section.
                        // The syntax is METRIC[,{TZ|LZ}][,{000.000|000.00|0000.00}] ??????
                        if (drillState.CurrentSection != DrillFileSection.Header)
                        {
                            break;
                        }

                        done = true;
                        lineReader.Position -= 2;       // Point back to the start on the line.
                        line    = lineReader.ReadLineToEnd();
                        command = line.Split(',');
                        if (command[0] == "METRIC")
                        {
                            drillState.Unit = GerberUnit.Millimeter;
                        }

                        if (command.Length > 1)
                        {
                            if (command[1] == "TZ")
                            {
                                if (drillState.AutoDetect)
                                {
                                    image.Format.OmitZeros = GerberOmitZero.OmitZerosLeading;
                                }

                                done = false;
                            }

                            else if (command[1] == "LZ")
                            {
                                if (drillState.AutoDetect)
                                {
                                    image.Format.OmitZeros = GerberOmitZero.OmitZerosTrailing;
                                }

                                done = false;
                            }

                            else
                            {
                                errorMessage = "Invalid zero suppression found after METRIC.\n";
                                stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning, lineReader.LineNumber, drillFileName);
                                done = true;
                            }

                            // Number format may or may not be specified.
                            if (!done && command.Length == 3)
                            {
                                if (drillState.AutoDetect)
                                {
                                    drillState.HeaderNumberFormat = drillState.DataNumberFormat = DrillNumberFormat.Format_000_000;
                                    drillState.DecimalPlaces      = 3;
                                }

                                if (command[2] == "0000.00")
                                {
                                    drillState.DataNumberFormat = DrillNumberFormat.Format_0000_00;
                                    drillState.DecimalPlaces    = 2;
                                }

                                else if (command[2] == "000.000")
                                {
                                    drillState.DataNumberFormat = DrillNumberFormat.Format_000_000;
                                    drillState.DecimalPlaces    = 3;
                                }

                                else if (command[2] == "000.00")
                                {
                                    drillState.DataNumberFormat = DrillNumberFormat.Format_000_00;
                                    drillState.DecimalPlaces    = 2;
                                }

                                else
                                {
                                    errorMessage = "Invalid number format found after TZ/LZ.\n";
                                    stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning, lineReader.LineNumber, drillFileName);
                                }
                            }
                        }

                        else
                        {
                            // No TZ/LZ or number format specified, use defaults.
                            if (drillState.AutoDetect)
                            {
                                image.Format.OmitZeros        = GerberOmitZero.OmitZerosLeading;
                                drillState.HeaderNumberFormat = DrillNumberFormat.Format_000_000;
                                drillState.DecimalPlaces      = 3;
                            }
                        }
                    }

                    else if (Char.IsDigit(nextCharacter))
                    {
                        // Must be an M## code.
                        lineReader.Position--;
                        DrillMCode mCode = ParseMCode(lineReader, drillState, image);
                        switch (mCode)
                        {
                        case DrillMCode.Header:
                            drillState.CurrentSection = DrillFileSection.Header;
                            break;

                        case DrillMCode.EndHeader:
                            drillState.CurrentSection = DrillFileSection.Data;
                            break;

                        case DrillMCode.Metric:
                            if (drillState.Unit == GerberUnit.Unspecified && drillState.CurrentSection != DrillFileSection.Header)
                            {
                                errorMessage = "M71 code found with no METRIC specification in header.\n";
                                stats.AddNewError(-1, errorMessage, GerberErrorType.GerberError, lineReader.LineNumber, drillFileName);

                                errorMessage = "Assuming all tool sizes are in millimeters.\n";
                                stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning);


                                for (int toolNumber = ToolMin; toolNumber < ToolMax; toolNumber++)
                                {
                                    if (image.ApertureArray[toolNumber] != null)
                                    {
                                        double toolSize = image.ApertureArray[toolNumber].Parameters[0];
                                        stats.ModifyDrillList(toolNumber, toolSize, "MM");
                                        image.ApertureArray[toolNumber].Parameters[0] /= 25.4;
                                    }
                                }
                            }

                            if (drillState.AutoDetect)
                            {
                                drillState.DataNumberFormat = drillState.BackupNumberFormat;
                                drillState.Unit             = GerberUnit.Millimeter;
                            }

                            break;

                        case DrillMCode.Imperial:
                            if (drillState.AutoDetect)
                            {
                                if (drillState.DataNumberFormat != DrillNumberFormat.Format_00_0000)
                                {
                                    drillState.BackupNumberFormat = drillState.DataNumberFormat;            // Save format definition for later.
                                }
                                drillState.DataNumberFormat = DrillNumberFormat.Format_00_0000;
                                drillState.DecimalPlaces    = 4;
                                drillState.Unit             = GerberUnit.Inch;
                            }
                            break;

                        case DrillMCode.LongMessage:
                        case DrillMCode.Message:
                        case DrillMCode.CannedText:
                            line = lineReader.ReadLineToEnd();
                            // message here.
                            break;

                        case DrillMCode.NotImplemented:
                        case DrillMCode.EndPattern:
                        case DrillMCode.TipCheck:
                            break;

                        case DrillMCode.End:
                            line = lineReader.ReadLineToEnd();
                            break;

                        case DrillMCode.EndRewind:          // EOF.
                            //done = true;
                            foundEOF = true;
                            break;

                        default:
                            stats.AddNewError(-1, "Undefined M code.", GerberErrorType.GerberError, lineReader.LineNumber, drillFileName);
                            break;
                        }
                    }
                    break;

                case 'R':
                    if (drillState.CurrentSection == DrillFileSection.Header)
                    {
                        stats.AddNewError(-1, "R code not allowed in the header.", GerberErrorType.GerberError, lineReader.LineNumber, drillFileName);
                    }

                    else
                    {
                        double stepX = 0.0, stepY = 0.0;
                        int    length = 0;

                        image.DrillStats.R++;
                        double startX      = drillState.CurrentX;
                        double startY      = drillState.CurrentY;
                        int    repeatcount = lineReader.GetIntegerValue(ref length);
                        nextCharacter = lineReader.Read();
                        if (nextCharacter == 'X')
                        {
                            stepX         = GetDoubleValue(lineReader, drillState.DataNumberFormat, image.Format.OmitZeros, drillState.DecimalPlaces);
                            nextCharacter = lineReader.Read();
                        }

                        if (nextCharacter == 'Y')
                        {
                            stepY = GetDoubleValue(lineReader, drillState.DataNumberFormat, image.Format.OmitZeros, drillState.DecimalPlaces);
                        }

                        else
                        {
                            lineReader.Position--;
                        }

                        for (int i = 1; i < repeatcount; i++)
                        {
                            drillState.CurrentX = startX + i * stepX;
                            drillState.CurrentY = startY + i * stepY;
                            currentNet          = AddDrillHole(image, drillState, currentNet);
                        }
                    }
                    break;

                case 'S':
                    // Ignore spindle speed.
                    lineReader.ReadLineToEnd();
                    break;

                case 'T':
                    int tool = ParseTCode(lineReader, drillFileName, drillState, image);
                    break;

                case 'X':
                case 'Y':
                    // Hole coordinate found. Do some parsing.
                    ParseCoordinate(lineReader, nextCharacter, image, drillState);

                    // Add the new drill hole.
                    currentNet = AddDrillHole(image, drillState, currentNet);
                    break;

                case '%':
                    drillState.CurrentSection = DrillFileSection.Data;
                    break;

                // Ignore white space or null characters.
                case '\n':
                case '\r':
                case ' ':
                case '\t':
                case '\0':
                    break;
                }
            }

            return(foundEOF);
        }