Gerbview: Attempt to parse unknown files as gerber/drill
Test parsing function transmogrified from gerbv 2.7.0. gEDA suite is GPL 2+ so should be license compatible with our GPL3. Fixes: https://gitlab.com/kicad/code/kicad/-/issues/1848
This commit is contained in:
parent
21a8dd6302
commit
da2e7e158b
|
@ -154,6 +154,16 @@ public: EXCELLON_IMAGE( int layer ) :
|
|||
virtual void ResetDefaultValues() override;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Performs a heuristics-based check of whether the file is an Excellon drill file.
|
||||
*
|
||||
* Does not invoke the full parser.
|
||||
*
|
||||
* @param aFullFileName aFullFileName is the full filename of the Excellon file.
|
||||
* @return True if drill file, false otherwise
|
||||
*/
|
||||
static bool TestFileIsExcellon( const wxString& aFullFileName );
|
||||
|
||||
/**
|
||||
* Read and load a drill (EXCELLON format) file.
|
||||
*
|
||||
|
|
|
@ -315,6 +315,131 @@ void EXCELLON_IMAGE::ResetDefaultValues()
|
|||
m_NoTrailingZeros = true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Original function derived from drill_file_p() of gerbv 2.7.0.
|
||||
* Copyright of the source file drill.cpp included below:
|
||||
*/
|
||||
/*
|
||||
* gEDA - GNU Electronic Design Automation
|
||||
* drill.c
|
||||
* Copyright (C) 2000-2006 Andreas Andersson
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
bool EXCELLON_IMAGE::TestFileIsExcellon( const wxString& aFullFileName )
|
||||
{
|
||||
char* letter;
|
||||
bool foundM48 = false;
|
||||
bool foundM30 = false;
|
||||
bool foundPercent = false;
|
||||
bool foundT = false;
|
||||
bool foundX = false;
|
||||
bool foundY = false;
|
||||
|
||||
FILE* file = wxFopen( aFullFileName, "rb" );
|
||||
|
||||
if( file == nullptr )
|
||||
return false;
|
||||
|
||||
FILE_LINE_READER excellonReader( file, aFullFileName );
|
||||
|
||||
try
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
if( excellonReader.ReadLine() == nullptr )
|
||||
break;
|
||||
|
||||
// Remove all whitespace from the beginning and end
|
||||
char* line = StrPurge( excellonReader.Line() );
|
||||
|
||||
// Skip empty lines
|
||||
if( *line == 0 )
|
||||
continue;
|
||||
|
||||
// Check that file is not binary (non-printing chars)
|
||||
for( size_t i = 0; i < strlen( line ); i++ )
|
||||
{
|
||||
if( !isascii( line[i] ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't want to look for any commands after a comment so
|
||||
// just end the line early if we find a comment
|
||||
char* buf = strstr( line, ";" );
|
||||
if( buf != nullptr )
|
||||
*buf = 0;
|
||||
|
||||
// Check for M48 = start of drill header
|
||||
if( strstr( line, "M48" ) )
|
||||
foundM48 = true;
|
||||
|
||||
// Check for M30 = end of drill program
|
||||
if( strstr( line, "M30" ) )
|
||||
if( foundPercent )
|
||||
foundM30 = true; // Found M30 after % = good
|
||||
|
||||
// Check for % on its own line at end of header
|
||||
if( ( letter = strstr( line, "%" ) ) != nullptr )
|
||||
if( ( letter[1] == '\r' ) || ( letter[1] == '\n' ) )
|
||||
foundPercent = true;
|
||||
|
||||
// Check for T<number>
|
||||
if( ( letter = strstr( line, "T" ) ) != nullptr )
|
||||
{
|
||||
if( !foundT && ( foundX || foundY ) )
|
||||
foundT = false; /* Found first T after X or Y */
|
||||
else
|
||||
{
|
||||
// verify next char is digit
|
||||
if( isdigit( letter[1] ) )
|
||||
foundT = true;
|
||||
}
|
||||
}
|
||||
|
||||
// look for X<number> or Y<number>
|
||||
if( ( letter = strstr( line, "X" ) ) != nullptr )
|
||||
if( isdigit( letter[1] ) )
|
||||
foundX = true;
|
||||
|
||||
if( ( letter = strstr( line, "Y" ) ) != nullptr )
|
||||
if( isdigit( letter[1] ) )
|
||||
foundY = true;
|
||||
}
|
||||
}
|
||||
catch( IO_ERROR& e )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Now form logical expression determining if this is a drill file */
|
||||
if( ( ( foundX || foundY ) && foundT ) && ( foundM48 || ( foundPercent && foundM30 ) ) )
|
||||
return true;
|
||||
else if( foundM48 && foundT && foundPercent && foundM30 )
|
||||
/* Pathological case of drill file with valid header
|
||||
and EOF but no drill XY locations. */
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read a EXCELLON file.
|
||||
* Gerber classes are used because there is likeness between Gerber files
|
||||
|
|
|
@ -529,17 +529,6 @@ bool GERBVIEW_FRAME::unarchiveFiles( const wxString& aFullFileName, REPORTER* aR
|
|||
enum GERBER_ORDER_ENUM order;
|
||||
GERBER_FILE_IMAGE_LIST::GetGerberLayerFromFilename( fname, order, matchedExt );
|
||||
|
||||
if( order == GERBER_ORDER_ENUM::GERBER_LAYER_UNKNOWN )
|
||||
{
|
||||
if( aReporter )
|
||||
{
|
||||
msg.Printf( _( "Skipped file '%s' (unknown type).\n" ), entry->GetName() );
|
||||
aReporter->Report( msg, RPT_SEVERITY_WARNING );
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int layer = GetActiveLayer();
|
||||
|
||||
if( layer == NO_AVAILABLE_LAYERS )
|
||||
|
@ -583,7 +572,33 @@ bool GERBVIEW_FRAME::unarchiveFiles( const wxString& aFullFileName, REPORTER* aR
|
|||
|
||||
bool read_ok = true;
|
||||
|
||||
if( order != GERBER_ORDER_ENUM::GERBER_DRILL )
|
||||
// Try to parse files if we can't tell from file extension
|
||||
if( order == GERBER_ORDER_ENUM::GERBER_LAYER_UNKNOWN )
|
||||
{
|
||||
if( EXCELLON_IMAGE::TestFileIsExcellon( unzipped_tempfile ) )
|
||||
{
|
||||
order = GERBER_ORDER_ENUM::GERBER_DRILL;
|
||||
}
|
||||
else if( GERBER_FILE_IMAGE::TestFileIsRS274( unzipped_tempfile ) )
|
||||
{
|
||||
// If we have no way to know what layer it is, just guess
|
||||
order = GERBER_ORDER_ENUM::GERBER_TOP_COPPER;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( aReporter )
|
||||
{
|
||||
msg.Printf( _( "Skipped file '%s' (unknown type).\n" ), entry->GetName() );
|
||||
aReporter->Report( msg, RPT_SEVERITY_WARNING );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( order == GERBER_ORDER_ENUM::GERBER_DRILL )
|
||||
{
|
||||
read_ok = Read_EXCELLON_File( unzipped_tempfile );
|
||||
}
|
||||
else if( order != GERBER_ORDER_ENUM::GERBER_LAYER_UNKNOWN )
|
||||
{
|
||||
// Read gerber files: each file is loaded on a new GerbView layer
|
||||
read_ok = Read_GERBER_File( unzipped_tempfile );
|
||||
|
@ -592,10 +607,6 @@ bool GERBVIEW_FRAME::unarchiveFiles( const wxString& aFullFileName, REPORTER* aR
|
|||
GetCanvas()->GetView()->SetLayerHasNegatives(
|
||||
GERBER_DRAW_LAYER( layer ), GetGbrImage( layer )->HasNegativeItems() );
|
||||
}
|
||||
else // Everything else is a drill file
|
||||
{
|
||||
read_ok = Read_EXCELLON_File( unzipped_tempfile );
|
||||
}
|
||||
|
||||
delete entry;
|
||||
|
||||
|
|
|
@ -118,6 +118,16 @@ public:
|
|||
return wxT( "GERBER_FILE_IMAGE" );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a heuristics-based check of whether the file is an RS274 gerber file.
|
||||
*
|
||||
* Does not invoke the full parser.
|
||||
*
|
||||
* @param aFullFileName aFullFileName is the full filename of the gerber file.
|
||||
* @return True if RS274 file, false otherwise
|
||||
*/
|
||||
static bool TestFileIsRS274( const wxString& aFullFileName );
|
||||
|
||||
/**
|
||||
* Read and load a gerber file.
|
||||
*
|
||||
|
|
|
@ -110,6 +110,122 @@ bool GERBVIEW_FRAME::Read_GERBER_File( const wxString& GERBER_FullFileName )
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Original function derived from gerber_is_rs274x_p() of gerbv 2.7.0.
|
||||
* Copyright of the source file readgerb.cpp included below:
|
||||
*/
|
||||
/* gEDA - GNU Electronic Design Automation
|
||||
* This is a part of gerbv
|
||||
*
|
||||
* Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
|
||||
*/
|
||||
bool GERBER_FILE_IMAGE::TestFileIsRS274( const wxString& aFullFileName )
|
||||
{
|
||||
char* letter;
|
||||
bool foundADD = false;
|
||||
bool foundD0 = false;
|
||||
bool foundD2 = false;
|
||||
bool foundM0 = false;
|
||||
bool foundM2 = false;
|
||||
bool foundStar = false;
|
||||
bool foundX = false;
|
||||
bool foundY = false;
|
||||
|
||||
|
||||
FILE* file = wxFopen( aFullFileName, "rb" );
|
||||
|
||||
if( file == nullptr )
|
||||
return false;
|
||||
|
||||
FILE_LINE_READER gerberReader( aFullFileName );
|
||||
|
||||
try
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
if( gerberReader.ReadLine() == nullptr )
|
||||
break;
|
||||
|
||||
// Remove all whitespace from the beginning and end
|
||||
char* line = StrPurge( gerberReader.Line() );
|
||||
|
||||
// Skip empty lines
|
||||
if( *line == 0 )
|
||||
continue;
|
||||
|
||||
// Check that file is not binary (non-printing chars)
|
||||
for( size_t i = 0; i < strlen( line ); i++ )
|
||||
{
|
||||
if( !isascii( line[i] ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( strstr( line, "%ADD" ) )
|
||||
foundADD = true;
|
||||
|
||||
if( strstr( line, "D00" ) || strstr( line, "D0" ) )
|
||||
foundD0 = true;
|
||||
|
||||
if( strstr( line, "D02" ) || strstr( line, "D2" ) )
|
||||
foundD2 = true;
|
||||
|
||||
if( strstr( line, "M00" ) || strstr( line, "M0" ) )
|
||||
foundM0 = true;
|
||||
|
||||
if( strstr( line, "M02" ) || strstr( line, "M2" ) )
|
||||
foundM2 = true;
|
||||
|
||||
if( strstr( line, "*" ) )
|
||||
foundStar = true;
|
||||
|
||||
/* look for X<number> or Y<number> */
|
||||
if( ( letter = strstr( line, "X" ) ) != nullptr )
|
||||
{
|
||||
if( isdigit( letter[1] ) )
|
||||
foundX = true;
|
||||
}
|
||||
|
||||
if( ( letter = strstr( line, "Y" ) ) != nullptr )
|
||||
{
|
||||
if( isdigit( letter[1] ) )
|
||||
foundY = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( IO_ERROR& e )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// RS-274X
|
||||
if( ( foundD0 || foundD2 || foundM0 || foundM2 ) && foundADD && foundStar
|
||||
&& ( foundX || foundY ) )
|
||||
return true;
|
||||
// RS-274D. Could be folded into the expression above, but someday
|
||||
// we might want to test for them separately.
|
||||
else if( ( foundD0 || foundD2 || foundM0 || foundM2 ) && !foundADD && foundStar
|
||||
&& ( foundX || foundY ) )
|
||||
return true;
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// size of a single line of text from a gerber file.
|
||||
// warning: some files can have *very long* lines, so the buffer must be large.
|
||||
|
|
Loading…
Reference in New Issue