1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2007-2019 Jean-Pierre Charras  jp.charras at wanadoo.fr
5  * Copyright (C) 1992-2019 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 /**
26  * @file job_file_reader.cpp
27  */
28 
29 #include <nlohmann/json.hpp>
30 #include <wx/filename.h>
31 
32 #include <wildcards_and_files_ext.h>
33 #include <gerbview.h>
34 #include <richio.h>
35 #include <locale_io.h>
36 #include <macros.h>
37 #include <gerber_file_image.h>
38 #include <gerber_file_image_list.h>
39 #include <gerbview_frame.h>
40 #include <reporter.h>
41 #include <gbr_metadata.h>
42 #include <dialogs/html_message_box.h>
43 #include <view/view.h>
44 #include <wx/filedlg.h>
45 
46 
47 using json = nlohmann::json;
48 
49 /**
50  * this class read and parse a Gerber job file to extract useful info
51  * for GerbView
52  *
53  * In a gerber job file, old (deprecated) format, data lines start by
54  * %TF.     (usual Gerber X2 info)
55  * %TJ.B.   (board info)
56  * %TJ.D.   (design info)
57  * %TJ.L.   (layers info)
58  * some others are not yet handled by Kicad
59  * M02*     is the last line
60 
61  * In a gerber job file, JSON format, first lines are
62  *   {
63  *    "Header":
64  * and the block ( a JSON array) containing the filename of files to load is
65  *    "FilesAttributes":
66  *    [
67  *      {
68  *        "Path":  "interf_u-Composant.gbr",
69  *        "FileFunction":  "Copper,L1,Top",
70  *        "FilePolarity":  "Positive"
71  *      },
72  *      {
73  *        "Path":  "interf_u-In1.Cu.gbr",
74  *        "FileFunction":  "Copper,L2,Inr",
75  *        "FilePolarity":  "Positive"
76  *      },
77  *    ],
78  */
79 
80 class GERBER_JOBFILE_READER
81 {
82 public:
GERBER_JOBFILE_READER(const wxString & aFileName,REPORTER * aReporter)83     GERBER_JOBFILE_READER( const wxString& aFileName, REPORTER* aReporter )
84     {
85         m_filename = aFileName;
86         m_reporter = aReporter;
87     }
88 
~GERBER_JOBFILE_READER()89     ~GERBER_JOBFILE_READER() {}
90 
91     bool ReadGerberJobFile();       /// read a .gbrjob file
GetGerberFiles()92     wxArrayString& GetGerberFiles() { return m_GerberFiles; }
93 
94 private:
95     REPORTER* m_reporter;
96     wxFileName m_filename;
97     wxArrayString m_GerberFiles;    // List of gerber files in job
98 
99     // Convert a JSON string, that uses escaped sequence of 4 hexadecimal digits
100     // to encode unicode chars when not ASCII7 codes
101     // json11 converts this sequence to UTF8 string
102     wxString formatStringFromJSON( const std::string& name );
103 };
104 
105 
ReadGerberJobFile()106 bool GERBER_JOBFILE_READER::ReadGerberJobFile()
107 {
108     // Read the gerber file */
109    FILE* jobFile = wxFopen( m_filename.GetFullPath(), wxT( "rt" ) );
110 
111     if( jobFile == nullptr )
112         return false;
113 
114     LOCALE_IO toggleIo;
115 
116     FILE_LINE_READER jobfileReader( jobFile, m_filename.GetFullPath() );  // Will close jobFile
117 
118     wxString msg;
119     wxString data;
120 
121     // detect the file format: old (deprecated) gerber format of official JSON format
122     bool json_format = false;
123 
124     char* line = jobfileReader.ReadLine();
125 
126     if( !line )     // end of file
127         return false;
128 
129     data = line;
130 
131     if( data.Contains( "{" ) )
132         json_format = true;
133 
134     if( json_format )
135     {
136         while( ( line = jobfileReader.ReadLine() ) )
137             data << '\n' << line;
138 
139         try
140         {
141             json js = json::parse( TO_UTF8( data ) );
142 
143             for( json& entry : js["FilesAttributes"] )
144             {
145                 std::string name = entry["Path"].get<std::string>();
146                 m_GerberFiles.Add( formatStringFromJSON( name ) );
147             }
148         }
149         catch( ... )
150         {
151             return false;
152         }
153     }
154     else
155     {
156         if( m_reporter )
157             m_reporter->ReportTail( _( "This job file uses an outdated format. Please recreate it." ),
158                                     RPT_SEVERITY_WARNING );
159 
160         return false;
161     }
162 
163     return true;
164 }
165 
166 
formatStringFromJSON(const std::string & name)167 wxString GERBER_JOBFILE_READER::formatStringFromJSON( const std::string& name )
168 {
169     // Convert a JSON string, that uses a escaped sequence of 4 hexadecimal digits
170     // to encode unicode chars
171     // Our json11 library returns in this case a UTF8 sequence. Just convert it to
172     // a wxString.
173     wxString wstr = FROM_UTF8( name.c_str() );
174     return wstr;
175 }
176 
177 
178 
LoadGerberJobFile(const wxString & aFullFileName)179 bool GERBVIEW_FRAME::LoadGerberJobFile( const wxString& aFullFileName )
180 {
181     wxFileName filename = aFullFileName;
182     wxString currentPath;
183     bool success = true;
184 
185     if( !filename.IsOk() )
186     {
187         // Use the current working directory if the file name path does not exist.
188         if( filename.DirExists() )
189             currentPath = filename.GetPath();
190         else
191             currentPath = m_mruPath;
192 
193         wxFileDialog dlg( this, _( "Open Gerber Job File" ),
194                           currentPath,
195                           filename.GetFullName(),
196                           GerberJobFileWildcard(),
197                           wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR );
198 
199         if( dlg.ShowModal() == wxID_CANCEL )
200             return false;
201 
202         filename = dlg.GetPath();
203         currentPath = filename.GetPath();
204         m_mruPath = currentPath;
205     }
206     else
207     {
208         currentPath = filename.GetPath();
209         m_mruPath = currentPath;
210     }
211 
212     wxString msg;
213     WX_STRING_REPORTER reporter( &msg );
214 
215     if( filename.IsOk() )
216     {
217         GERBER_JOBFILE_READER gbjReader( filename.GetFullPath(), &reporter );
218 
219         if( gbjReader.ReadGerberJobFile() )
220         {
221             // Update the list of recent drill files.
222             UpdateFileHistory( filename.GetFullPath(), &m_jobFileHistory );
223 
224             Clear_DrawLayers( false );
225             ClearMsgPanel();
226 
227             wxArrayString& gbrfiles = gbjReader.GetGerberFiles();
228 
229             success = LoadListOfGerberAndDrillFiles( currentPath, gbrfiles );
230 
231             Zoom_Automatique( false );
232         }
233     }
234 
235     SortLayersByX2Attributes();
236 
237     if( !msg.IsEmpty() )
238     {
239         wxSafeYield();  // Allows slice of time to redraw the screen
240                         // to refresh widgets, before displaying messages
241         HTML_MESSAGE_BOX mbox( this, _( "Messages" ) );
242         mbox.ListSet( msg );
243         mbox.ShowModal();
244     }
245 
246     return success;
247 }
248