← Back to team overview

kicad-developers team mailing list archive

[PATCH] GerbView non-standard extensions

 

Attached is a patch to allow GerbView to "correctly" process %FSD
commands.  It also displays a warning message after processing the file
that the Gerber is invalid.

Background:
- %FSD is used by older versions of Altium Designer [1], PADS, Allergro and
​CircuitCAM, among others, to denote that coordinate zeros should include
_both_ leading a trailing zeros.  While this has never been part of the
RS274 standard, it is understood by some manufacturers[2] and is parsed by
other gerber viewers[3][4][5].

While we don't want to generate these invalid files, the attached patch
will display them as intended by the EDAs that generated them.  I've
verified against ECAMII v6.0 and PADS X2 with the files from [6][7][8].

-Seth

[1]
http://www.altium.com/documentation/sites/default/files/wiki_attachments/255441/Gerber%20Advanced%20AD.png
[2] https://www.eurocircuits.com/rs274x-extended-gerber/
[3] www.gerber-viewer.com/
[4] https://circuitpeople.com/
[5] http://gerbv.geda-project.org/
[6]
https://github.com/spqr/umichmoo/blob/master/schematics/moo1_1/altium-progheader/Gerbers/programmer.gbs
[7]
https://github.com/sonyxperiadev/CDB-Assist/blob/master/Hardware/CDBAssist-v3.1-OutlineRoute.gbr
[8] http://zeus.pd.infn.it/MVD/download/pitch/pitch.ger
From 56db5d0f1233732a3a506ffedc2b7e25574db408 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <hillbrand@xxxxxxxxxxx>
Date: Thu, 28 Sep 2017 15:38:36 -0700
Subject: [PATCH 1/1] gerbview: Corrects handling of %FSD statement

---
 gerbview/class_X2_gerber_attributes.cpp       |  4 ++-
 gerbview/class_X2_gerber_attributes.h         |  3 +-
 gerbview/class_gerber_file_image.cpp          |  2 +-
 gerbview/class_gerber_file_image.h            | 17 ++++++++--
 gerbview/excellon_read_drill_file.cpp         |  3 +-
 gerbview/job_file_reader.cpp                  |  3 --
 gerbview/readgerb.cpp                         |  1 +
 gerbview/rs274_read_XY_and_IJ_coordinates.cpp |  2 +-
 gerbview/rs274d.cpp                           |  2 +-
 gerbview/rs274x.cpp                           | 49 ++++++++++++---------------
 10 files changed, 47 insertions(+), 39 deletions(-)

diff --git a/gerbview/class_X2_gerber_attributes.cpp b/gerbview/class_X2_gerber_attributes.cpp
index fef61ede4..09c3a290f 100644
--- a/gerbview/class_X2_gerber_attributes.cpp
+++ b/gerbview/class_X2_gerber_attributes.cpp
@@ -95,7 +95,8 @@ void X2_ATTRIBUTE::DbgListPrms()
  * buff = the buffer containing current Gerber data (GERBER_BUFZ size)
  * text = a pointer to the first char to read in Gerber data
  */
-bool X2_ATTRIBUTE::ParseAttribCmd( FILE* aFile, char *aBuffer, int aBuffSize, char* &aText )
+bool X2_ATTRIBUTE::ParseAttribCmd( FILE* aFile, char *aBuffer, int aBuffSize, char* &aText,
+        int& aLineNum )
 {
     bool ok = true;
     wxString data;
@@ -144,6 +145,7 @@ bool X2_ATTRIBUTE::ParseAttribCmd( FILE* aFile, char *aBuffer, int aBuffSize, ch
                 break;
             }
 
+            aLineNum++;
             aText = aBuffer;
         }
         else
diff --git a/gerbview/class_X2_gerber_attributes.h b/gerbview/class_X2_gerber_attributes.h
index 25b2a8e8b..4f43971c0 100644
--- a/gerbview/class_X2_gerber_attributes.h
+++ b/gerbview/class_X2_gerber_attributes.h
@@ -96,9 +96,10 @@ public:
      * @param aText = a pointer to the first char to read from Gerber data stored in aBuffer
      *  After parsing, text points the last char of the command line ('%') (X2 mode)
      *  or the end of line if the line does not contain '%' or aBuffer == NULL (X1 mode)
+     * @oaram aLineNum = a point to the current line number of aFile
      * @return true if no error.
      */
-    bool ParseAttribCmd( FILE* aFile, char *aBuffer, int aBuffSize, char* &aText );
+    bool ParseAttribCmd( FILE* aFile, char *aBuffer, int aBuffSize, char* &aText, int& aLineNum );
 
     /**
      * Debug function: pring using wxLogMessage le list of parameters
diff --git a/gerbview/class_gerber_file_image.cpp b/gerbview/class_gerber_file_image.cpp
index 0e35991ae..4d2b18b0b 100644
--- a/gerbview/class_gerber_file_image.cpp
+++ b/gerbview/class_gerber_file_image.cpp
@@ -195,7 +195,6 @@ void GERBER_FILE_IMAGE::ResetDefaultValues()
     m_Relative = false;                             // false = absolute Coord,
                                                     // true = relative Coord
     m_NoTrailingZeros = false;                      // true: trailing zeros deleted
-    m_DecimalFormat = false;                        // true: use floating point notations for coordinates
     m_ImageOffset.x   = m_ImageOffset.y = 0;        // Coord Offset, from IO command
     m_ImageRotation = 0;                            // Allowed 0, 90, 180, 270 (in degree)
     m_LocalRotation = 0.0;                          // Layer totation from RO command (in 0.1 degree)
@@ -220,6 +219,7 @@ void GERBER_FILE_IMAGE::ResetDefaultValues()
     m_PreviousPos.x = m_PreviousPos.y = 0;          // last specified coord
     m_IJPos.x = m_IJPos.y = 0;                      // current centre coord for
                                                     // plot arcs & circles
+    m_LineNum = 0;                                  // line number in file being read
     m_Current_File    = NULL;                       // Gerber file to read
     m_PolygonFillMode = false;
     m_PolygonFillModeState = 0;
diff --git a/gerbview/class_gerber_file_image.h b/gerbview/class_gerber_file_image.h
index 6d83ae91c..a0f4798e3 100644
--- a/gerbview/class_gerber_file_image.h
+++ b/gerbview/class_gerber_file_image.h
@@ -125,8 +125,6 @@ public:
     bool               m_GerbMetric;                            // false = Inches, true = metric
     bool               m_Relative;                              // false = absolute Coord, true = relative Coord
     bool               m_NoTrailingZeros;                       // true: remove tailing zeros.
-    bool               m_DecimalFormat;                         // true: use floating point notations for coordinates
-                                                                // If true, overrides m_NoTrailingZeros parameter.
     wxPoint            m_ImageOffset;                           // Coord Offset, from IO command
     wxSize             m_FmtScale;                              // Fmt 2.3: m_FmtScale = 3, fmt 3.4: m_FmtScale = 4
     wxSize             m_FmtLen;                                // Nb chars per coord. ex fmt 2.3, m_FmtLen = 5
@@ -143,6 +141,7 @@ public:
     int                m_Current_Tool;                          // Current Tool (Dcode) number selected
     int                m_Last_Pen_Command;                      // Current or last pen state (0..9, set by Dn option with n <10
     int                m_CommandState;                          // state of gerber analysis command.
+    int                m_LineNum;                               // Line number of the gerber file while reading
     wxPoint            m_CurrentPos;                            // current specified coord for plot
     wxPoint            m_PreviousPos;                           // old current specified coord for plot
     wxPoint            m_IJPos;                                 // IJ coord (for arcs & circles )
@@ -175,6 +174,20 @@ private:
                                                                 // -1 = negative items are
                                                                 // 0 = no negative items found
                                                                 // 1 = have negative items found
+    /**
+     * 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
+    */
+    char* GetNextLine( char *aBuff, char* aText, FILE* aFile );
+
+    bool GetEndOfBlock( char* buff, char*& text, FILE* gerber_file );
 
 public:
     GERBER_FILE_IMAGE( int layer );
diff --git a/gerbview/excellon_read_drill_file.cpp b/gerbview/excellon_read_drill_file.cpp
index 6f949b6e2..950a10a7d 100644
--- a/gerbview/excellon_read_drill_file.cpp
+++ b/gerbview/excellon_read_drill_file.cpp
@@ -307,7 +307,8 @@ bool EXCELLON_IMAGE::LoadFile( const wxString & aFullFileName )
     // 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 );
+    int dummyline = 0;
+    dummy.ParseAttribCmd( m_Current_File, NULL, 0, text, dummyline );
     delete m_FileFunction;
     m_FileFunction = new X2_ATTRIBUTE_FILEFUNCTION( dummy );
 
diff --git a/gerbview/job_file_reader.cpp b/gerbview/job_file_reader.cpp
index faf731d19..d4741ad33 100644
--- a/gerbview/job_file_reader.cpp
+++ b/gerbview/job_file_reader.cpp
@@ -39,9 +39,6 @@
 #include <plot_auxiliary_data.h>
 #include <html_messagebox.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 );
 
 /**
  * this class read and parse a Gerber job file to extract useful info
diff --git a/gerbview/readgerb.cpp b/gerbview/readgerb.cpp
index f55c10f74..031711efa 100644
--- a/gerbview/readgerb.cpp
+++ b/gerbview/readgerb.cpp
@@ -128,6 +128,7 @@ bool GERBER_FILE_IMAGE::LoadGerberFile( const wxString& aFullFileName )
         if( fgets( line, sizeof(line), m_Current_File ) == NULL )
             break;
 
+        m_LineNum++;
         text = StrPurge( line );
 
         while( text && *text )
diff --git a/gerbview/rs274_read_XY_and_IJ_coordinates.cpp b/gerbview/rs274_read_XY_and_IJ_coordinates.cpp
index c92cd63e9..8e6289b6b 100644
--- a/gerbview/rs274_read_XY_and_IJ_coordinates.cpp
+++ b/gerbview/rs274_read_XY_and_IJ_coordinates.cpp
@@ -73,7 +73,7 @@ wxPoint GERBER_FILE_IMAGE::ReadXYCoord( char*& Text )
 {
     wxPoint pos;
     int     type_coord = 0, current_coord, nbdigits;
-    bool    is_float   = m_DecimalFormat;
+    bool    is_float   = false;
     char*   text;
     char    line[256];
 
diff --git a/gerbview/rs274d.cpp b/gerbview/rs274d.cpp
index 3d66bf8fe..8b79c92f3 100644
--- a/gerbview/rs274d.cpp
+++ b/gerbview/rs274d.cpp
@@ -484,7 +484,7 @@ bool GERBER_FILE_IMAGE::Execute_G_Command( char*& text, int G_command )
         {
             text += 7;
             X2_ATTRIBUTE dummy;
-            dummy.ParseAttribCmd( m_Current_File, NULL, 0, text );
+            dummy.ParseAttribCmd( m_Current_File, NULL, 0, text, m_LineNum );
             if( dummy.IsFileFunction() )
             {
                 delete m_FileFunction;
diff --git a/gerbview/rs274x.cpp b/gerbview/rs274x.cpp
index fca35208b..37c236c6e 100644
--- a/gerbview/rs274x.cpp
+++ b/gerbview/rs274x.cpp
@@ -37,7 +37,6 @@
 
 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) )
@@ -233,7 +232,7 @@ bool GERBER_FILE_IMAGE::ReadRS274XCommand( char* buff, char*& text )
             ok = false;
             break;
         }
-
+        m_LineNum++;
         text = buff;
     }
 
@@ -273,14 +272,21 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int command, char* buff, char*& te
                 text++;
                 break;
 
+            case 'D':       // Non-standard option for all zeros (leading + tailing)
+                msg.Printf( _( "RS274X: Invalid GERBER format command '%c' at line %d: \"%s\"" ),
+                        'D', m_LineNum, buff );
+                AddMessageToList( msg );
+                msg.Printf( _("GERBER file \"%s\" may not display as intended." ),
+                        m_FileName.ToAscii() );
+                AddMessageToList( msg );
+                // Fallthrough
+
             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;
@@ -305,16 +311,12 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int command, char* buff, char*& te
                     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':
@@ -411,7 +413,7 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int command, char* buff, char*& te
         m_IsX2_file = true;
     {
         X2_ATTRIBUTE dummy;
-        dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text );
+        dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text, m_LineNum );
 
         if( dummy.IsFileFunction() )
         {
@@ -432,7 +434,7 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int command, char* buff, char*& te
     case APERTURE_ATTRIBUTE:    // Command %TA ... Not yet supported
         {
         X2_ATTRIBUTE dummy;
-        dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text );
+        dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text, m_LineNum );
 
         if( dummy.GetAttribute() == ".AperFunction" )
         {
@@ -449,7 +451,7 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int command, char* buff, char*& te
         {
         X2_ATTRIBUTE dummy;
 
-        dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text );
+        dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text, m_LineNum );
 
         if( dummy.GetAttribute() == ".N" )
         {
@@ -473,7 +475,7 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int command, char* buff, char*& te
     case REMOVE_APERTURE_ATTRIBUTE:    // Command %TD ...
         {
         X2_ATTRIBUTE dummy;
-        dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text );
+        dummy.ParseAttribCmd( m_Current_File, buff, GERBER_BUFZ, text, m_LineNum );
         RemoveAttribute( dummy );
         }
         break;
@@ -902,7 +904,7 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int command, char* buff, char*& te
 }
 
 
-bool GetEndOfBlock( char* buff, char*& text, FILE* gerber_file )
+bool GERBER_FILE_IMAGE::GetEndOfBlock( char* buff, char*& text, FILE* gerber_file )
 {
     for( ; ; )
     {
@@ -920,6 +922,7 @@ bool GetEndOfBlock( char* buff, char*& text, FILE* gerber_file )
         if( fgets( buff, GERBER_BUFZ, gerber_file ) == NULL )
             break;
 
+        m_LineNum++;
         text = buff;
     }
 
@@ -927,18 +930,7 @@ bool GetEndOfBlock( char* buff, char*& text, FILE* gerber_file )
 }
 
 
-/**
- * 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  )
+char* GERBER_FILE_IMAGE::GetNextLine( char *aBuff, char* aText, FILE* aFile )
 {
     for( ; ; )
     {
@@ -953,6 +945,8 @@ static char* GetNextLine(  char *aBuff, char* aText, FILE* aFile  )
             case 0:    // End of text found in aBuff: Read a new string
                 if( fgets( aBuff, GERBER_BUFZ, aFile ) == NULL )
                     return NULL;
+
+                m_LineNum++;
                 aText = aBuff;
                 return aText;
 
@@ -1078,9 +1072,8 @@ bool GERBER_FILE_IMAGE::ReadApertureMacro( char *buff,
             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 ) ) );
+            msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Invalid primitive id code %d, line %d: \"%s\"" ),
+                        GetChars( am.name ), primitive_type, m_LineNum, GetChars( FROM_UTF8( buff ) ) );
             AddMessageToList( msg );
             return false;
         }
-- 
2.11.0


Follow ups