KiCad PCB EDA Suite
gerber_placefile_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) 2019 Jean_Pierre Charras <jp.charras at wanadoo.fr>
5 * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
27
28#include <vector>
29
31#include <string_utils.h>
32#include <locale_io.h>
33#include <macros.h>
34
35#include <board.h>
37#include <pcb_shape.h>
38
39#include <pcbplot.h>
41#include <gbr_metadata.h>
42#include <footprint.h>
43#include <pad.h>
44#include <fp_shape.h>
45
46
48{
49 m_pcb = aPcb;
50 m_plotPad1Marker = true; // Place a marker to pin 1 (or A1) position
51 m_plotOtherPadsMarker = true; // Place a marker to other pins position
52 m_layer = PCB_LAYER_ID::UNDEFINED_LAYER; // No layer set
53}
54
55
56int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER_ID aLayer,
57 bool aIncludeBrdEdges )
58{
59 m_layer = aLayer;
60
62
63 if( plotOpts.GetUseAuxOrigin() )
65
66 // Collect footprints on the right layer
67 std::vector<FOOTPRINT*> fp_list;
68
69 for( FOOTPRINT* footprint : m_pcb->Footprints() )
70 {
71 if( footprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
72 continue;
73
74 if( footprint->GetLayer() == aLayer )
75 fp_list.push_back( footprint );
76 }
77
78 LOCALE_IO dummy_io; // Use the standard notation for float numbers
79
80 GERBER_PLOTTER plotter;
81
82 // Gerber drill file imply X2 format:
83 plotter.UseX2format( true );
84 plotter.UseX2NetAttributes( true );
85
86 // Add the standard X2 header, without FileFunction
87 AddGerberX2Header( &plotter, m_pcb );
88 plotter.SetViewport( m_offset, pcbIUScale.IU_PER_MILS/10, /* scale */ 1.0, /* mirror */false );
89
90 // has meaning only for gerber plotter. Must be called only after SetViewport
91 plotter.SetGerberCoordinatesFormat( 6 );
92 plotter.SetCreator( wxT( "PCBNEW" ) );
93
94 // Add the standard X2 FileFunction for P&P files
95 // %TF.FileFunction,Component,Ln,[top][bottom]*%
96 wxString text;
97 text.Printf( wxT( "%%TF.FileFunction,Component,L%d,%s*%%" ),
98 aLayer == B_Cu ? m_pcb->GetCopperLayerCount() : 1,
99 aLayer == B_Cu ? wxT( "Bot" ) : wxT( "Top" ) );
100 plotter.AddLineToHeader( text );
101
102 // Add file polarity (positive)
103 text = wxT( "%TF.FilePolarity,Positive*%" );
104 plotter.AddLineToHeader( text );
105
106 if( !plotter.OpenFile( aFullFilename ) )
107 return -1;
108
109 // We need a BRDITEMS_PLOTTER to plot pads
110 BRDITEMS_PLOTTER brd_plotter( &plotter, m_pcb, plotOpts );
111
112 plotter.StartPlot( wxT( "1" ) );
113
114 // Some tools in P&P files have the type and size defined.
115 // they are position flash (round), pad1 flash (diamond), other pads flash (round)
116 // and component outline thickness (polyline)
117 int flash_position_shape_diam = pcbIUScale.mmToIU( 0.3 ); // defined size for position shape (circle)
118 int pad1_mark_size = pcbIUScale.mmToIU( 0.36 ); // defined size for pad 1 position (diamond)
119 int other_pads_mark_size = 0; // defined size for position shape (circle)
120 int line_thickness = pcbIUScale.mmToIU( 0.1 ); // defined size for component outlines
121
122 brd_plotter.SetLayerSet( LSET( aLayer ) );
123 int cmp_count = 0;
124 bool allowUtf8 = true;
125
126 // Plot components data: position, outlines, pad1 and other pads.
127 for( FOOTPRINT* footprint : fp_list )
128 {
129 // Manage the aperture attribute component position:
130 GBR_METADATA gbr_metadata;
132
133 // Add object attribute: component reference to flash (mainly useful for users)
134 // using quoted UTF8 string
135 wxString ref = ConvertNotAllowedCharsInGerber( footprint->Reference().GetShownText(),
136 allowUtf8, true );
137
138 gbr_metadata.SetCmpReference( ref );
140
141 // Add P&P specific attributes
142 GBR_CMP_PNP_METADATA pnpAttrib;
143
144 // Add rotation info (rotation is CCW, in degrees):
145 pnpAttrib.m_Orientation = mapRotationAngle( footprint->GetOrientationDegrees(),
146 aLayer == B_Cu ? true : false );
147
149
150 if( footprint->GetAttributes() & FP_THROUGH_HOLE )
152 else if( footprint->GetAttributes() & FP_SMD )
154
155 // Add component value info:
156 pnpAttrib.m_Value = ConvertNotAllowedCharsInGerber( footprint->Value().GetShownText(),
157 allowUtf8, true );
158
159 // Add component footprint info:
160 wxString fp_info = FROM_UTF8( footprint->GetFPID().GetLibItemName().c_str() );
161 pnpAttrib.m_Footprint = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, true );
162
163 // Add footprint lib name:
164 fp_info = FROM_UTF8( footprint->GetFPID().GetLibNickname().c_str() );
165 pnpAttrib.m_LibraryName = ConvertNotAllowedCharsInGerber( fp_info, allowUtf8, true );
166
167 gbr_metadata.m_NetlistMetadata.SetExtraData( pnpAttrib.FormatCmpPnPMetadata() );
168
169 VECTOR2I flash_pos = footprint->GetPosition();
170
171 plotter.FlashPadCircle( flash_pos, flash_position_shape_diam, FILLED, &gbr_metadata );
172 gbr_metadata.m_NetlistMetadata.ClearExtraData();
173
174 // Now some extra metadata is output, avoid blindly clearing the full metadata list
176
177 // We plot the footprint courtyard when possible.
178 // If not, the pads bounding box will be used.
179 bool useFpPadsBbox = true;
180 bool onBack = aLayer == B_Cu;
181
182 footprint->BuildCourtyardCaches();
183
184 int checkFlag = onBack ? MALFORMED_B_COURTYARD : MALFORMED_F_COURTYARD;
185
186 if( ( footprint->GetFlags() & checkFlag ) == 0 )
187 {
188 gbr_metadata.SetApertureAttrib(
190
191 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( aLayer );
192
193 for( int ii = 0; ii < courtyard.OutlineCount(); ii++ )
194 {
195 SHAPE_LINE_CHAIN poly = courtyard.Outline( ii );
196
197 if( !poly.PointCount() )
198 continue;
199
200 useFpPadsBbox = false;
201 plotter.PLOTTER::PlotPoly( poly, FILL_T::NO_FILL, line_thickness, &gbr_metadata );
202 }
203 }
204
205 if( useFpPadsBbox )
206 {
207 gbr_metadata.SetApertureAttrib(
209
210 // bbox of fp pads, pos 0, rot 0, non flipped
211 BOX2I bbox = footprint->GetFpPadsLocalBbox();
212
213 // negate bbox Y values if the fp is flipped (always flipped around X axis
214 // in Gerber P&P files).
215 int y_sign = aLayer == B_Cu ? -1 : 1;
216
217 SHAPE_LINE_CHAIN poly;
218 poly.Append( bbox.GetLeft(), y_sign*bbox.GetTop() );
219 poly.Append( bbox.GetLeft(), y_sign*bbox.GetBottom() );
220 poly.Append( bbox.GetRight(), y_sign*bbox.GetBottom() );
221 poly.Append( bbox.GetRight(), y_sign*bbox.GetTop() );
222 poly.SetClosed( true );
223
224 poly.Rotate( footprint->GetOrientation() );
225 poly.Move( footprint->GetPosition() );
226 plotter.PLOTTER::PlotPoly( poly, FILL_T::NO_FILL, line_thickness, &gbr_metadata );
227 }
228
229 std::vector<PAD*>pad_key_list;
230
231 if( m_plotPad1Marker )
232 {
233 findPads1( pad_key_list, footprint );
234
235 for( PAD* pad1 : pad_key_list )
236 {
237 gbr_metadata.SetApertureAttrib(
239
240 gbr_metadata.SetPadName( pad1->GetNumber(), allowUtf8, true );
241
242 gbr_metadata.SetPadPinFunction( pad1->GetPinFunction(), allowUtf8, true );
243
245
246 // Flashes a diamond at pad position:
247 plotter.FlashRegularPolygon( pad1->GetPosition(), pad1_mark_size, 4, ANGLE_0,
248 FILLED, &gbr_metadata );
249 }
250 }
251
253 {
254 gbr_metadata.SetApertureAttrib(
257
258 for( PAD* pad: footprint->Pads() )
259 {
260 bool skip_pad = false;
261
262 for( PAD* pad1 : pad_key_list )
263 {
264 if( pad == pad1 ) // Already plotted
265 {
266 skip_pad = true;
267 break;
268 }
269 }
270
271 if( skip_pad )
272 continue;
273
274 // Skip also pads not on the current layer, like pads only
275 // on a tech layer
276 if( !pad->IsOnLayer( aLayer ) )
277 continue;
278
279 gbr_metadata.SetPadName( pad->GetNumber(), allowUtf8, true );
280
281 gbr_metadata.SetPadPinFunction( pad->GetPinFunction(), allowUtf8, true );
282
283 // Flashes a round, 0 sized round shape at pad position
284 plotter.FlashPadCircle( pad->GetPosition(), other_pads_mark_size, FILLED,
285 &gbr_metadata );
286 }
287 }
288
289 plotter.ClearAllAttributes(); // Unconditionally close all .TO attributes
290
291 cmp_count++;
292 }
293
294 // Plot board outlines, if requested
295 if( aIncludeBrdEdges )
296 {
297 brd_plotter.SetLayerSet( LSET( Edge_Cuts ) );
298
299 // Plot edge layer and graphic items
300 brd_plotter.PlotBoardGraphicItems();
301
302 // Draw footprint other graphic items:
303 for( FOOTPRINT* footprint : fp_list )
304 {
305 for( BOARD_ITEM* item : footprint->GraphicalItems() )
306 {
307 if( item->Type() == PCB_FP_SHAPE_T && item->GetLayer() == Edge_Cuts )
308 brd_plotter.PlotFootprintShape( static_cast<FP_SHAPE*>( item ) );
309 }
310 }
311 }
312
313 plotter.EndPlot();
314
315 return cmp_count;
316}
317
318
319double PLACEFILE_GERBER_WRITER::mapRotationAngle( double aAngle, bool aIsFlipped )
320{
321 // Convert a KiCad footprint orientation to gerber rotation, depending on the layer
322 // Gerber rotation is:
323 // rot angle > 0 for rot CW, seen from Top side
324 // same a Pcbnew for Top side
325 // (angle + 180) for Bottom layer i.e flipped around Y axis: X axis coordinates mirrored.
326 // because Pcbnew flip around the X axis : Y coord mirrored, that is similar to mirror
327 // around Y axis + 180 deg rotation
328 if( aIsFlipped )
329 {
330 double gbr_angle = 180.0 + aAngle;
331
332 // Normalize between -180 ... + 180 deg
333 // Not mandatory, but the angle is more easy to read
334 if( gbr_angle <= -180 )
335 gbr_angle += 360.0;
336 else if( gbr_angle > 180 )
337 gbr_angle -= 360.0;
338
339 return gbr_angle;
340 }
341
342 return aAngle;
343}
344
345
346void PLACEFILE_GERBER_WRITER::findPads1( std::vector<PAD*>& aPadList, FOOTPRINT* aFootprint ) const
347{
348 // Fint the pad "1" or pad "A1"
349 // this is possible only if only one pad is found
350 // useful to place a marker in this position
351
352 for( PAD* pad : aFootprint->Pads() )
353 {
354 if( !pad->IsOnLayer( m_layer ) )
355 continue;
356
357 if( pad->GetNumber() == wxT( "1" ) || pad->GetNumber() == wxT( "A1" ) )
358 aPadList.push_back( pad );
359 }
360}
361
362
363const wxString PLACEFILE_GERBER_WRITER::GetPlaceFileName( const wxString& aFullBaseFilename,
364 PCB_LAYER_ID aLayer ) const
365{
366 // Gerber files extension is always .gbr.
367 // Therefore, to mark pnp files, add "-pnp" to the filename, and a layer id.
368 wxFileName fn = aFullBaseFilename;
369
370 wxString post_id = wxT( "-pnp_" );
371 post_id += aLayer == B_Cu ? wxT( "bottom" ) : wxT( "top" );
372 fn.SetName( fn.GetName() + post_id );
373 fn.SetExt( GerberFileExtension );
374
375 return fn.GetFullPath();
376}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
const VECTOR2I & GetAuxOrigin()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:58
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
FOOTPRINTS & Footprints()
Definition: board.h:307
int GetCopperLayerCount() const
Definition: board.cpp:499
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition: board.h:627
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:628
coord_type GetTop() const
Definition: box2.h:194
coord_type GetRight() const
Definition: box2.h:189
coord_type GetLeft() const
Definition: box2.h:193
coord_type GetBottom() const
Definition: box2.h:190
void SetLayerSet(LSET aLayerMask)
Definition: pcbplot.h:80
void PlotFootprintShape(const FP_SHAPE *aShape)
void PlotBoardGraphicItems()
Plot items like text and graphics but not tracks and footprints.
PADS & Pads()
Definition: footprint.h:170
@ GBR_APERTURE_ATTRIB_PAD1_POSITION
aperture used for flashed pads position in placement files.
Definition: gbr_metadata.h:149
@ GBR_APERTURE_ATTRIB_CMP_POSITION
aperture used for flashed pin 1 (or A1 or AA1) position in placement files.
Definition: gbr_metadata.h:146
@ GBR_APERTURE_ATTRIB_PADOTHER_POSITION
aperture used to draw component physical body outline without pins in placement files.
Definition: gbr_metadata.h:152
@ GBR_APERTURE_ATTRIB_CMP_FOOTPRINT
aperture used to draw component outline courtyard in placement files.
Definition: gbr_metadata.h:161
Information which can be added in a gerber P&P file as attribute of a component.
wxString FormatCmpPnPMetadata()
Metadata which can be added in a gerber file as attribute in X2 format.
Definition: gbr_metadata.h:205
void SetCmpReference(const wxString &aComponentRef)
Definition: gbr_metadata.h:241
void SetPadPinFunction(const wxString &aPadPinFunction, bool aUseUTF8, bool aEscapeString)
Definition: gbr_metadata.h:236
void SetApertureAttrib(GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB aApertAttribute)
Definition: gbr_metadata.h:209
GBR_NETLIST_METADATA m_NetlistMetadata
An item to handle object attribute.
Definition: gbr_metadata.h:262
void SetNetAttribType(int aNetAttribType)
Definition: gbr_metadata.h:219
void SetPadName(const wxString &aPadname, bool aUseUTF8=false, bool aEscapeString=false)
Definition: gbr_metadata.h:231
@ GBR_NETINFO_CMP
print info associated to a component (TO.C attribute)
@ GBR_NETINFO_PAD
print info associated to a flashed pad (TO.P attribute)
void ClearExtraData()
Clear the extra data string printed at end of net attributes.
bool m_TryKeepPreviousAttributes
If true, do not clear all attributes when a attribute has changed.
void SetExtraData(const wxString &aExtraData)
Set the extra data string printed at end of net attributes.
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false) override
Selection of Gerber units and resolution (number of digits in mantissa).
void ClearAllAttributes()
Remove (clear) all attributes from object attributes dictionary (TO.
virtual void FlashPadCircle(const VECTOR2I &pos, int diametre, OUTLINE_MODE trace_mode, void *aData) override
Filled circular flashes are stored as apertures.
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 FlashRegularPolygon(const VECTOR2I &aShapePos, int aDiameter, int aCornerCount, const EDA_ANGLE &aOrient, OUTLINE_MODE aTraceMode, void *aData) override
Flash a regular polygon.
virtual bool StartPlot(const wxString &pageNumber) override
Write GERBER header to file initialize global variable g_Plot_PlotOutputFile.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:41
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
Definition: pad.h:59
Parameters and options when plotting/printing a board.
bool GetUseAuxOrigin() const
const wxString GetPlaceFileName(const wxString &aFullBaseFilename, PCB_LAYER_ID aLayer) const
int CreatePlaceFile(wxString &aFullFilename, PCB_LAYER_ID aLayer, bool aIncludeBrdEdges)
Create an pnp gerber file.
double mapRotationAngle(double aAngle, bool aIsFlipped)
Convert a KiCad footprint orientation to gerber rotation both are in degrees.
void findPads1(std::vector< PAD * > &aPadList, FOOTPRINT *aFootprint) const
Find the pad(s) 1 (or pad "A1") of a footprint.
virtual bool OpenFile(const wxString &aFullFilename)
Open or create the plot file aFullFilename.
Definition: plotter.cpp:74
virtual void SetCreator(const wxString &aCreator)
Definition: plotter.h:159
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:168
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:412
#define MALFORMED_F_COURTYARD
#define MALFORMED_B_COURTYARD
@ FP_SMD
Definition: footprint.h:69
@ FP_EXCLUDE_FROM_POS_FILES
Definition: footprint.h:70
@ FP_THROUGH_HOLE
Definition: footprint.h:68
wxString ConvertNotAllowedCharsInGerber(const wxString &aString, bool aAllowUtf8Chars, bool aQuoteString)
Normalize aString and convert it to a Gerber compatible wxString.
Handle special data (items attributes) during plot.
Classes used in place file generation.
const std::string GerberFileExtension
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ Edge_Cuts
Definition: layer_ids.h:113
@ B_Cu
Definition: layer_ids.h:95
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
This file contains miscellaneous commonly used macros and functions.
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
@ FILLED
Definition: outline_mode.h:27
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:276
Plotting engine (Gerber)
const double IU_PER_MILS
Definition: base_units.h:78
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
@ PCB_FP_SHAPE_T
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:94
Definition of file extensions used in Kicad.