1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2007-2016 Jean-Pierre Charras  jp.charras at wanadoo.fr
5  * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
23  */
24 
25 #include <string_utils.h>
26 #include <locale_io.h>
27 #include <gerbview.h>
28 #include <gerbview_frame.h>
29 #include <gerber_file_image.h>
30 #include <gerber_file_image_list.h>
31 #include <view/view.h>
32 
33 #include <dialogs/html_message_box.h>
34 #include <macros.h>
35 
36 #include <wx/msgdlg.h>
37 
38 /* Read a gerber file, RS274D, RS274X or RS274X2 format.
39  */
Read_GERBER_File(const wxString & GERBER_FullFileName)40 bool GERBVIEW_FRAME::Read_GERBER_File( const wxString& GERBER_FullFileName )
41 {
42     wxString msg;
43 
44     int layer = GetActiveLayer();
45     GERBER_FILE_IMAGE_LIST* images = GetImagesList();
46     GERBER_FILE_IMAGE* gerber = GetGbrImage( layer );
47 
48     if( gerber != nullptr )
49     {
50         Erase_Current_DrawLayer( false );
51     }
52 
53     // use an unique ptr while we load to free on exception properly
54     std::unique_ptr<GERBER_FILE_IMAGE> gerber_uptr = std::make_unique<GERBER_FILE_IMAGE>( layer );
55 
56     // Read the gerber file. The image will be added only if it can be read
57     // to avoid broken data.
58     bool success = gerber_uptr->LoadGerberFile( GERBER_FullFileName );
59 
60     if( !success )
61     {
62         gerber_uptr.reset();
63         msg.Printf( _( "File '%s' not found" ), GERBER_FullFileName );
64         ShowInfoBarError( msg );
65         return false;
66     }
67 
68     gerber = gerber_uptr.release();
69     wxASSERT( gerber != nullptr );
70     images->AddGbrImage( gerber, layer );
71 
72     // Display errors list
73     if( gerber->GetMessages().size() > 0 )
74     {
75         HTML_MESSAGE_BOX dlg( this, _( "Errors" ) );
76         dlg.ListSet( gerber->GetMessages() );
77         dlg.ShowModal();
78     }
79 
80     /* if the gerber file has items using D codes but missing D codes definitions,
81      * it can be a deprecated RS274D file (i.e. without any aperture information),
82      * or has missing definitions,
83      * warn the user:
84      */
85     if( gerber->GetItemsCount() && gerber->m_Has_MissingDCode )
86     {
87         if( !gerber->m_Has_DCode )
88             msg = _("Warning: this file has no D-Code definition\n"
89                     "Therefore the size of some items is undefined");
90         else
91             msg = _("Warning: this file has some missing D-Code definitions\n"
92                     "Therefore the size of some items is undefined");
93 
94         wxMessageBox( msg );
95     }
96 
97     if( GetCanvas() )
98     {
99         if( gerber->m_ImageNegative )
100         {
101             // TODO: find a way to handle negative images
102             // (maybe convert geometry into positives?)
103         }
104 
105         for( auto item : gerber->GetItems() )
106             GetCanvas()->GetView()->Add( (KIGFX::VIEW_ITEM*) item );
107     }
108 
109     return true;
110 }
111 
112 
113 
114 // size of a single line of text from a gerber file.
115 // warning: some files can have *very long* lines, so the buffer must be large.
116 #define GERBER_BUFZ 1000000
117 // A large buffer to store one line
118 static char lineBuffer[GERBER_BUFZ+1];
119 
LoadGerberFile(const wxString & aFullFileName)120 bool GERBER_FILE_IMAGE::LoadGerberFile( const wxString& aFullFileName )
121 {
122     int      G_command = 0;        // command number for G commands like G04
123     int      D_commande = 0;       // command number for D commands like D02
124     char*    text;
125 
126     ClearMessageList( );
127     ResetDefaultValues();
128 
129     // Read the gerber file */
130     m_Current_File = wxFopen( aFullFileName, wxT( "rt" ) );
131 
132     if( m_Current_File == nullptr )
133         return false;
134 
135     m_FileName = aFullFileName;
136 
137     LOCALE_IO toggleIo;
138 
139     wxString msg;
140 
141     while( true )
142     {
143         if( fgets( lineBuffer, GERBER_BUFZ, m_Current_File ) == nullptr )
144             break;
145 
146         m_LineNum++;
147         text = StrPurge( lineBuffer );
148 
149         while( text && *text )
150         {
151             switch( *text )
152             {
153             case ' ':
154             case '\r':
155             case '\n':
156                 text++;
157                 break;
158 
159             case '*':       // End command
160                 m_CommandState = END_BLOCK;
161                 text++;
162                 break;
163 
164             case 'M':       // End file
165                 m_CommandState = CMD_IDLE;
166                 while( *text )
167                     text++;
168                 break;
169 
170             case 'G':    /* Line type Gxx : command */
171                 G_command = GCodeNumber( text );
172                 Execute_G_Command( text, G_command );
173                 break;
174 
175             case 'D':       /* Line type Dxx : Tool selection (xx > 0) or
176                              * command if xx = 0..9 */
177                 D_commande = DCodeNumber( text );
178                 Execute_DCODE_Command( text, D_commande );
179                 break;
180 
181             case 'X':
182             case 'Y':                   /* Move or draw command */
183                 m_CurrentPos = ReadXYCoord( text );
184                 if( *text == '*' )      // command like X12550Y19250*
185                 {
186                     Execute_DCODE_Command( text, m_Last_Pen_Command );
187                 }
188                 break;
189 
190             case 'I':
191             case 'J':       /* Auxiliary Move command */
192                 m_IJPos = ReadIJCoord( text );
193 
194                 if( *text == '*' )      // command like X35142Y15945J504*
195                 {
196                     Execute_DCODE_Command( text, m_Last_Pen_Command );
197                 }
198                 break;
199 
200             case '%':
201                 if( m_CommandState != ENTER_RS274X_CMD )
202                 {
203                     m_CommandState = ENTER_RS274X_CMD;
204                     ReadRS274XCommand( lineBuffer, GERBER_BUFZ, text );
205                 }
206                 else        //Error
207                 {
208                     AddMessageToList( "Expected RS274X Command"  );
209                     m_CommandState = CMD_IDLE;
210                     text++;
211                 }
212                 break;
213 
214             default:
215                 msg.Printf( "Unexpected char 0x%2.2X &lt;%c&lt;", *text, *text );
216                 AddMessageToList( msg );
217                 text++;
218                 break;
219             }
220         }
221     }
222 
223     fclose( m_Current_File );
224 
225     m_InUse = true;
226 
227     return true;
228 }
229