KiCad PCB EDA Suite
Loading...
Searching...
No Matches
gendrill_gerber_writer.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
5 * Copyright The 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
29
31#include <string_utils.h>
32#include <locale_io.h>
33#include <board.h>
34#include <footprint.h>
35#include <pcb_track.h>
36#include <pad.h>
37#include <pcbplot.h>
39#include <reporter.h>
40#include <gbr_metadata.h>
41
42// set to 1 to use flashed oblong holes, 0 to draw them by a line (route holes).
43// WARNING: currently ( gerber-layer-format-specification-revision-2023-08 ),
44// oblong holes **must be routed* in a drill file and not flashed,
45// so set FLASH_OVAL_HOLE to 0
46#define FLASH_OVAL_HOLE 0
47
48
58
59
60bool GERBER_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory, bool aGenDrill,
61 bool aGenMap, bool aGenTenting, REPORTER* aReporter )
62{
63 bool success = true;
64 // Note: In Gerber drill files, NPTH and PTH are always separate files
65 m_merge_PTH_NPTH = false;
66
67 wxFileName fn;
68 wxString msg;
69
70 std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();
71
72 // append a pair representing the NPTH set of holes, for separate drill files.
73 // (Gerber drill files are separate files for PTH and NPTH)
74 hole_sets.emplace_back( F_Cu, B_Cu );
75
76 for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin();
77 it != hole_sets.end(); ++it )
78 {
79 DRILL_LAYER_PAIR pair = *it;
80 // For separate drill files, the last layer pair is the NPTH drill file.
81 bool doing_npth = ( it == hole_sets.end() - 1 );
82
83 buildHolesList( pair, doing_npth );
84
85 // The file is created if it has holes, or if it is the non plated drill file
86 // to be sure the NPTH file is up to date in separate files mode.
87 // Also a PTH drill/map file is always created, to be sure at least one plated hole drill
88 // file is created (do not create any PTH drill file can be seen as not working drill
89 // generator).
90 if( getHolesCount() > 0 || doing_npth || pair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
91 {
92 fn = getDrillFileName( pair, doing_npth, false );
93 fn.SetPath( aPlotDirectory );
94
95 if( aGenDrill )
96 {
97 wxString fullFilename = fn.GetFullPath();
98
99 int result = createDrillFile( fullFilename, doing_npth, pair );
100
101 if( result < 0 )
102 {
103 if( aReporter )
104 {
105 msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
106 aReporter->Report( msg, RPT_SEVERITY_ERROR );
107 success = false;
108 }
109
110 break;
111 }
112 else
113 {
114 if( aReporter )
115 {
116 msg.Printf( _( "Created file '%s'." ), fullFilename );
117 aReporter->Report( msg, RPT_SEVERITY_ACTION );
118 }
119 }
120 }
121 }
122
123 if( getHolesCount() > 0 && !doing_npth )
124 {
125 for( IPC4761_FEATURES feature :
130 {
131 if( !aGenTenting )
132 {
133 if( feature == IPC4761_FEATURES::TENTED_BACK
134 || feature == IPC4761_FEATURES::TENTED_FRONT )
135 {
136 continue;
137 }
138 }
139
140 if( !hasViaType( feature ) )
141 continue;
142
143 fn = getProtectionFileName( pair, feature );
144 fn.SetPath( aPlotDirectory );
145
146 wxString fullFilename = fn.GetFullPath();
147
148 if( createProtectionFile( fullFilename, feature, pair ) < 0 )
149 {
150 if( aReporter )
151 {
152 msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
153 aReporter->Report( msg, RPT_SEVERITY_ERROR );
154 success = false;
155 }
156 }
157 else
158 {
159 if( aReporter )
160 {
161 msg.Printf( _( "Created file '%s'." ), fullFilename );
162 aReporter->Report( msg, RPT_SEVERITY_ACTION );
163 }
164 }
165 }
166 }
167 }
168
169 if( aGenMap )
170 success &= CreateMapFilesSet( aPlotDirectory, aReporter );
171
172 if( aReporter )
173 aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
174
175 return success;
176}
177
178
179#if !FLASH_OVAL_HOLE
180// A helper class to transform an oblong hole to a segment
181static void convertOblong2Segment( const VECTOR2I& aSize, const EDA_ANGLE& aOrient, VECTOR2I& aStart, VECTOR2I& aEnd );
182#endif
183
184int GERBER_WRITER::createProtectionFile( const wxString& aFullFilename, IPC4761_FEATURES aFeature,
185 DRILL_LAYER_PAIR aLayerPair )
186{
187 GERBER_PLOTTER plotter;
188 // Gerber drill file imply X2 format:
189 plotter.UseX2format( true );
190 plotter.UseX2NetAttributes( true );
191 plotter.DisableApertMacros( false );
192
193 // Add the standard X2 header, without FileFunction
194 AddGerberX2Header( &plotter, m_pcb );
195 plotter.SetViewport( m_offset, pcbIUScale.IU_PER_MILS / 10, /* scale */ 1.0,
196 /* mirror */ false );
197
198 // has meaning only for gerber plotter. Must be called only after SetViewport
199 plotter.SetGerberCoordinatesFormat( 6 );
200 plotter.SetCreator( wxT( "PCBNEW" ) );
201
202 // Add the standard X2 FileFunction for drill files
203 // %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Rout][Mixed]*%
204 wxString text = "%TF,FileFunction,Other,";
205
206 std::string attrib;
207 switch( aFeature )
208 {
210 text << wxT( "Capping" );
211 attrib = "Capping";
212 break;
214 text << wxT( "Filling" );
215 attrib = "Filling";
216 break;
218 text << wxT( "Covering-Back" );
219 attrib = "Covering";
220 break;
222 text << wxT( "Covering-Front" );
223 attrib = "Covering";
224 break;
226 text << wxT( "Plugging-Back" );
227 attrib = "Plugging";
228 break;
230 text << wxT( "Plugging-Front" );
231 attrib = "Plugging";
232 break;
234 text << wxT( "Tenting-Back" );
235 attrib = "Tenting";
236 break;
238 text << wxT( "Tenting-Front" );
239 attrib = "Tenting";
240 break;
241 default: return -1;
242 }
243 text << wxT( "*%" );
244 plotter.AddLineToHeader( text );
245
246 // Add file polarity (positive)
247 text = wxT( "%TF.FilePolarity,Positive*%" );
248 plotter.AddLineToHeader( text );
249
250
251 if( !plotter.OpenFile( aFullFilename ) )
252 return -1;
253
254 plotter.StartPlot( wxT( "1" ) );
255
256 int holes_count = 0;
257
258 for( auto& hole_descr : m_holeListBuffer )
259 {
260 if( !dyn_cast<const PCB_VIA*>( hole_descr.m_ItemParent ) )
261 {
262 continue;
263 }
264
265 const PCB_VIA* via = dyn_cast<const PCB_VIA*>( hole_descr.m_ItemParent );
266
267 bool cont = false;
268 int diameter = hole_descr.m_Hole_Diameter;
269 // clang-format off: suggestion is inconsitent
270 switch( aFeature )
271 {
273 cont = ! hole_descr.m_Hole_Filled;
274 break;
276 cont = ! hole_descr.m_Hole_Capped;
277 break;
279 cont = !hole_descr.m_Hole_Bot_Covered;
280 diameter = via->GetWidth( via->BottomLayer() );
281 break;
283 cont = ! hole_descr.m_Hole_Top_Covered;
284 diameter = via->GetWidth( via->TopLayer() );
285 break;
287 cont = !hole_descr.m_Hole_Bot_Plugged;
288 break;
290 cont = ! hole_descr.m_Hole_Top_Plugged;
291 break;
293 cont = ! hole_descr.m_Hole_Bot_Tented;
294 diameter = via->GetWidth( via->BottomLayer() );
295 break;
297 cont = ! hole_descr.m_Hole_Top_Tented;
298 diameter = via->GetWidth( via->TopLayer() );
299 break;
300 }
301 // clang-format on: suggestion is inconsitent
302
303 if( cont )
304 continue;
305
306 GBR_METADATA gbr_metadata;
307
308 gbr_metadata.SetApertureAttrib( attrib );
309
310 plotter.FlashPadCircle( hole_descr.m_Hole_Pos, diameter, &gbr_metadata );
311
312 holes_count++;
313 }
314
315 plotter.EndPlot();
316
317 return holes_count;
318}
319
320int GERBER_WRITER::createDrillFile( wxString& aFullFilename, bool aIsNpth,
321 DRILL_LAYER_PAIR aLayerPair )
322{
323 int holes_count;
324
325 LOCALE_IO dummy; // Use the standard notation for double numbers
326
327 GERBER_PLOTTER plotter;
328
329 // Gerber drill file imply X2 format:
330 plotter.UseX2format( true );
331 plotter.UseX2NetAttributes( true );
332 plotter.DisableApertMacros( false );
333
334 // Add the standard X2 header, without FileFunction
335 AddGerberX2Header( &plotter, m_pcb );
336 plotter.SetViewport( m_offset, pcbIUScale.IU_PER_MILS/10, /* scale */ 1.0, /* mirror */false );
337
338 // has meaning only for gerber plotter. Must be called only after SetViewport
339 plotter.SetGerberCoordinatesFormat( 6 );
340 plotter.SetCreator( wxT( "PCBNEW" ) );
341
342 // Add the standard X2 FileFunction for drill files
343 // %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Rout][Mixed]*%
344 wxString text = BuildFileFunctionAttributeString( aLayerPair,
345 aIsNpth ? TYPE_FILE::NPTH_FILE
347 plotter.AddLineToHeader( text );
348
349 // Add file polarity (positive)
350 text = wxT( "%TF.FilePolarity,Positive*%" );
351 plotter.AddLineToHeader( text );
352
353 if( !plotter.OpenFile( aFullFilename ) )
354 return -1;
355
356 plotter.StartPlot( wxT( "1" ) );
357
358 holes_count = 0;
359
360 VECTOR2I hole_pos;
361 bool last_item_is_via = true; // a flag to clear object attributes when a via hole is created.
362
363 for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
364 {
365 HOLE_INFO& hole_descr = m_holeListBuffer[ii];
366 hole_pos = hole_descr.m_Hole_Pos;
367
368 // Manage the aperture attributes: in drill files 3 attributes can be used:
369 // "ViaDrill", only for vias, not pads
370 // "ComponentDrill", only for Through Holes pads
371 // "Slot" for oblong holes;
372 GBR_METADATA gbr_metadata;
373
374 if( dyn_cast<const PCB_VIA*>( hole_descr.m_ItemParent ) )
375 {
377
378 if( !last_item_is_via )
379 {
380 // be sure the current object attribute is cleared for vias
381 plotter.EndBlock( nullptr );
382 }
383
384 last_item_is_via = true;
385 }
386 else if( dyn_cast<const PAD*>( hole_descr.m_ItemParent ) )
387 {
388 last_item_is_via = false;
389 const PAD* pad = dyn_cast<const PAD*>( hole_descr.m_ItemParent );
390
391 if( pad->GetProperty() == PAD_PROP::CASTELLATED )
392 {
393 gbr_metadata.SetApertureAttrib(
395 }
396 else if( pad->GetProperty() == PAD_PROP::PRESSFIT )
397 {
398 gbr_metadata.SetApertureAttrib(
400 }
401 else
402 {
403 // Good practice of oblong pad holes (slots) is to use a specific aperture for
404 // routing, not used in drill commands.
405 if( hole_descr.m_Hole_Shape )
406 {
407 gbr_metadata.SetApertureAttrib(
409 }
410 else
411 {
412 gbr_metadata.SetApertureAttrib(
414 }
415 }
416
417 // Add object attribute: component reference to pads (mainly useful for users)
418 wxString ref = pad->GetParentFootprint()->GetReference();
419
420 gbr_metadata.SetCmpReference( ref );
422 }
423
424 if( hole_descr.m_Hole_Shape )
425 {
426#if FLASH_OVAL_HOLE // set to 1 to use flashed oblong holes,
427 // 0 to draw them as a line.
428 plotter.FlashPadOval( hole_pos, hole_descr.m_Hole_Size, hole_descr.m_Hole_Orient,
429 &gbr_metadata );
430#else
431 // Use routing for oblong hole (Slots)
432 VECTOR2I start, end;
433 convertOblong2Segment( hole_descr.m_Hole_Size, hole_descr.m_Hole_Orient, start, end );
434 int width = std::min( hole_descr.m_Hole_Size.x, hole_descr.m_Hole_Size.y );
435
436 if ( width == 0 )
437 continue;
438
439 plotter.ThickSegment( start+hole_pos, end+hole_pos, width, &gbr_metadata );
440#endif
441 }
442 else
443 {
444 int diam = std::min( hole_descr.m_Hole_Size.x, hole_descr.m_Hole_Size.y );
445 plotter.FlashPadCircle( hole_pos, diam, &gbr_metadata );
446 }
447
448 holes_count++;
449 }
450
451 plotter.EndPlot();
452
453 return holes_count;
454}
455
456
457#if !FLASH_OVAL_HOLE
458void convertOblong2Segment( const VECTOR2I& aSize, const EDA_ANGLE& aOrient, VECTOR2I& aStart,
459 VECTOR2I& aEnd )
460{
461 VECTOR2I size( aSize );
462 EDA_ANGLE orient( aOrient );
463
464 /* The pad will be drawn as an oblong shape with size.y > size.x
465 * (Oval vertical orientation 0)
466 */
467 if( size.x > size.y )
468 {
469 std::swap( size.x, size.y );
470 orient += ANGLE_90;
471 }
472
473 int deltaxy = size.y - size.x; // distance between centers of the oval
474 aStart = VECTOR2I( 0, deltaxy / 2 );
475 RotatePoint( aStart, orient );
476
477 aEnd = VECTOR2I( 0, -deltaxy / 2 );
478 RotatePoint( aEnd, orient );
479}
480#endif
481
482
483void GERBER_WRITER::SetFormat( int aRightDigits )
484{
485 /* Set conversion scale depending on drill file units */
486 m_conversionUnits = 1.0 / pcbIUScale.IU_PER_MM; // Gerber units = mm
487
488 // Set precision (unit is mm).
489 m_precision.m_Lhs = 4;
490 m_precision.m_Rhs = aRightDigits == 6 ? 6 : 5;
491}
492
493
494const wxString GERBER_WRITER::getDrillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
495 bool aMerge_PTH_NPTH ) const
496{
497 // Gerber files extension is always .gbr.
498 // Therefore, to mark drill files, add "-drl" to the filename.
499 wxFileName fname( GENDRILL_WRITER_BASE::getDrillFileName( aPair, aNPTH, aMerge_PTH_NPTH ) );
500 fname.SetName( fname.GetName() + wxT( "-drl" ) );
501
502 return fname.GetFullPath();
503}
504
505
507{
508 for( auto& hole_descr : m_holeListBuffer )
509 {
510 if( !dyn_cast<const PCB_VIA*>( hole_descr.m_ItemParent ) )
511 {
512 continue;
513 }
514
515 switch( aFeature )
516 {
518 if( hole_descr.m_Hole_Filled )
519 return true;
520 break;
521
523 if( hole_descr.m_Hole_Capped )
524 return true;
525 break;
526
528 if( hole_descr.m_Hole_Bot_Covered )
529 return true;
530 break;
531
533 if( hole_descr.m_Hole_Top_Covered )
534 return true;
535 break;
536
538 if( hole_descr.m_Hole_Bot_Plugged )
539 return true;
540 break;
541
543 if( hole_descr.m_Hole_Top_Plugged )
544 return true;
545 break;
546
548 if( hole_descr.m_Hole_Bot_Tented )
549 return true;
550 break;
551
553 if( hole_descr.m_Hole_Top_Tented )
554 return true;
555 break;
556 }
557 }
558
559 return false;
560}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:317
@ GBR_APERTURE_ATTRIB_CMP_DRILL
Aperture used for pad holes in drill files.
@ GBR_APERTURE_ATTRIB_PRESSFITDRILL
Aperture used for pressfit pads in drill files.
@ GBR_APERTURE_ATTRIB_CMP_OBLONG_DRILL
Aperture used for pads oblong holes in drill files.
@ GBR_APERTURE_ATTRIB_CASTELLATEDDRILL
Aperture used for castellated pads in drill files.
@ GBR_APERTURE_ATTRIB_VIADRILL
Aperture used for via holes in drill files.
Metadata which can be added in a gerber file as attribute in X2 format.
void SetCmpReference(const wxString &aComponentRef)
void SetApertureAttrib(GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB aApertAttribute)
void SetNetAttribType(int aNetAttribType)
@ GBR_NETINFO_CMP
print info associated to a component (TO.C attribute)
std::vector< DRILL_LAYER_PAIR > getUniqueLayerPairs() const
Get unique layer pairs by examining the micro and blind_buried vias.
virtual const wxString getDrillFileName(DRILL_LAYER_PAIR aPair, bool aNPTH, bool aMerge_PTH_NPTH) const
void buildHolesList(DRILL_LAYER_PAIR aLayerPair, bool aGenerateNPTH_list)
Create the list of holes and tools for a given board.
const wxString BuildFileFunctionAttributeString(DRILL_LAYER_PAIR aLayerPair, TYPE_FILE aHoleType, bool aCompatNCdrill=false) const
virtual const wxString getProtectionFileName(DRILL_LAYER_PAIR aPair, IPC4761_FEATURES aFeature) const
std::vector< HOLE_INFO > m_holeListBuffer
bool CreateMapFilesSet(const wxString &aPlotDirectory, REPORTER *aReporter=nullptr)
Create the full set of map files for the board, in PS, PDF ... format (use SetMapFileFormat() to sele...
virtual void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, void *aData) override
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false) override
Selection of Gerber units and resolution (number of digits in mantissa).
virtual void FlashPadCircle(const VECTOR2I &pos, int diametre, void *aData) override
Filled circular flashes are stored as apertures.
virtual void FlashPadOval(const VECTOR2I &aPadPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, void *aData) override
virtual void SetViewport(const VECTOR2I &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
Set the plot offset and scaling for the current plot.
virtual bool EndPlot() override
void UseX2format(bool aEnable)
void UseX2NetAttributes(bool aEnable)
virtual void EndBlock(void *aData) override
Define the end of a group of drawing items the group is started by StartBlock().
virtual bool StartPlot(const wxString &pageNumber) override
Write GERBER header to file initialize global variable g_Plot_PlotOutputFile.
void DisableApertMacros(bool aDisable)
Disable Aperture Macro (AM) command, only for broken Gerber Readers.
bool CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, bool aGenTenting, REPORTER *aReporter=nullptr)
Create the full set of Excellon drill file for the board filenames are computed from the board name,...
virtual const wxString getDrillFileName(DRILL_LAYER_PAIR aPair, bool aNPTH, bool aMerge_PTH_NPTH) const override
int createDrillFile(wxString &aFullFilename, bool aIsNpth, DRILL_LAYER_PAIR aLayerPair)
Create an Excellon drill file.
void SetFormat(int aRightDigits=6)
Initialize internal parameters to match the given format.
int createProtectionFile(const wxString &aFullFilename, IPC4761_FEATURES aFeature, DRILL_LAYER_PAIR aLayerPair)
Create a Gerber X2 file for via protection features.
bool hasViaType(IPC4761_FEATURES aFeature)
test for an existing via having the given feature IPC4761_FEATURES
Handle hole which must be drilled (diameter, position and layers).
BOARD_ITEM * m_ItemParent
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:41
Definition pad.h:54
virtual bool OpenFile(const wxString &aFullFilename)
Open or create the plot file aFullFilename.
Definition plotter.cpp:76
virtual void SetCreator(const wxString &aCreator)
Definition plotter.h:173
void AddLineToHeader(const wxString &aExtraString)
Add a line to the list of free lines to print at the beginning of the file.
Definition plotter.h:183
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
virtual REPORTER & ReportTail(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Places the report at the end of the list, for objects that support report ordering.
Definition reporter.h:112
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
Handle special data (items attributes) during plot.
std::pair< PCB_LAYER_ID, PCB_LAYER_ID > DRILL_LAYER_PAIR
static void convertOblong2Segment(const VECTOR2I &aSize, const EDA_ANGLE &aOrient, VECTOR2I &aStart, VECTOR2I &aEnd)
Classes used in drill files, map files and report files generation.
@ B_Cu
Definition layer_ids.h:65
@ F_Cu
Definition layer_ids.h:64
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:107
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:105
void AddGerberX2Header(PLOTTER *aPlotter, const BOARD *aBoard, bool aUseX1CompatibilityMode)
Calculate some X2 attributes as defined in the Gerber file format specification J4 (chapter 5) and ad...
Definition pcbplot.cpp:294
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
std::vector< FAB_LAYER_COLOR > dummy
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:61
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695