KiCad PCB EDA Suite
gerber_jobfile_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) 2018 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
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 
30 #include <fstream>
31 #include <iomanip>
32 #include <vector>
33 
34 #include <build_version.h>
35 #include <locale_io.h>
36 #include <pcb_edit_frame.h>
37 #include <plotters/plotter.h>
38 
39 #include <board.h>
40 #include <board_design_settings.h>
41 #include <footprint.h>
42 #include <pad.h>
43 #include <pcb_track.h>
44 #include <zone.h>
45 
47 #include <gbr_metadata.h>
48 #include <gerber_jobfile_writer.h>
49 #include <pcbplot.h>
50 #include <reporter.h>
52 
53 
55 {
56  m_pcb = aPcb;
57  m_reporter = aReporter;
58  m_conversionUnits = 1.0 / IU_PER_MM; // Gerber units = mm
59 }
60 
61 std::string GERBER_JOBFILE_WRITER::formatStringFromUTF32( const wxString& aText )
62 {
63  std::string fmt_text; // the text after UTF32 to UTF8 conversion
64 
65  for( unsigned long letter : aText )
66  {
67  if( letter >= ' ' && letter <= 0x7F )
68  fmt_text += char( letter );
69  else
70  {
71  char buff[16];
72  sprintf( buff, "\\u%4.4lX", letter );
73  fmt_text += buff;
74  }
75  }
76  return fmt_text;
77 }
78 
79 
81 {
82  int flag = SIDE_NONE;
83 
84  for( unsigned ii = 0; ii < m_params.m_LayerId.size(); ii++ )
85  {
86  if( m_params.m_LayerId[ii] == B_SilkS )
87  flag |= SIDE_BOTTOM;
88 
89  if( m_params.m_LayerId[ii] == F_SilkS )
90  flag |= SIDE_TOP;
91  }
92 
93  return (enum ONSIDE) flag;
94 }
95 
96 
98 {
99  int flag = SIDE_NONE;
100 
101  for( unsigned ii = 0; ii < m_params.m_LayerId.size(); ii++ )
102  {
103  if( m_params.m_LayerId[ii] == B_Mask )
104  flag |= SIDE_BOTTOM;
105 
106  if( m_params.m_LayerId[ii] == F_Mask )
107  flag |= SIDE_TOP;
108  }
109 
110  return (enum ONSIDE) flag;
111 }
112 
113 const char* GERBER_JOBFILE_WRITER::sideKeyValue( enum ONSIDE aValue )
114 {
115  // return the key associated to sides used for some layers
116  // "No, TopOnly, BotOnly or Both"
117  const char* value = nullptr;
118 
119  switch( aValue )
120  {
121  case SIDE_NONE:
122  value = "No";
123  break;
124 
125  case SIDE_TOP:
126  value = "TopOnly";
127  break;
128 
129  case SIDE_BOTTOM:
130  value = "BotOnly";
131  break;
132 
133  case SIDE_BOTH:
134  value = "Both";
135  break;
136  }
137 
138  return value;
139 }
140 
141 
142 bool GERBER_JOBFILE_WRITER::CreateJobFile( const wxString& aFullFilename )
143 {
144  bool success;
145  wxString msg;
146 
147  success = WriteJSONJobFile( aFullFilename );
148 
149  if( !success )
150  {
151  if( m_reporter )
152  {
153  msg.Printf( _( "Failed to create file '%s'." ), aFullFilename );
155  }
156  }
157  else if( m_reporter )
158  {
159  msg.Printf( _( "Created Gerber job file '%s'." ), aFullFilename );
161  }
162 
163  return success;
164 }
165 
166 
168 {
169  wxString text;
170 
171  m_json["Header"] = {
172  {
173  "GenerationSoftware",
174  {
175  { "Vendor", "KiCad" },
176  { "Application", "Pcbnew" },
177  { "Version", GetBuildVersion() }
178  }
179  },
180  {
181  // The attribute value must conform to the full version of the ISO 8601
182  // date and time format, including time and time zone.
184  }
185  };
186 }
187 
188 
189 bool GERBER_JOBFILE_WRITER::WriteJSONJobFile( const wxString& aFullFilename )
190 {
191  // Note: in Gerber job file, dimensions are in mm, and are floating numbers
192  std::ofstream file( aFullFilename.ToUTF8() );
193 
195 
196  m_json = nlohmann::ordered_json( {} );
197 
198  // output the job file header
199  addJSONHeader();
200 
201  // Add the General Specs
203 
204  // Job file support a few design rules:
206 
207  // output the gerber file list:
209 
210  // output the board stackup:
212 
213  file << std::setw( 2 ) << m_json << std::endl;
214 
215  return true;
216 }
217 
218 
219 double GERBER_JOBFILE_WRITER::mapValue( double aUiValue )
220 {
221  // A helper function to convert aUiValue in Json units (mm) and to have
222  // 4 digits in Json in mantissa when using %g to print it
223  // i.e. displays values truncated in 0.1 microns.
224  // This is enough for a Json file
225  char buffer[128];
226  sprintf( buffer, "%.4f", aUiValue * m_conversionUnits );
227 
228  long double output;
229  sscanf( buffer, "%Lg", &output );
230 
231  return output;
232 
233 }
234 
235 
237 {
238  m_json["GeneralSpecs"] = nlohmann::ordered_json( {} );
239  m_json["GeneralSpecs"]["ProjectId"] = nlohmann::ordered_json( {} );
240 
241  // Creates the ProjectId. Format is (from Gerber file format doc):
242  // ProjectId,<project id>,<project GUID>,<revision id>*%
243  // <project id> is the name of the project, restricted to basic ASCII symbols only,
244  // and comma not accepted
245  // All illegal chars will be replaced by underscore
246  // Rem: <project id> accepts only ASCII 7 code (only basic ASCII codes are allowed in gerber files).
247  //
248  // <project GUID> is a string which is an unique id of a project.
249  // However Kicad does not handle such a project GUID, so it is built from the board name
250  wxFileName fn = m_pcb->GetFileName();
251  wxString msg = fn.GetFullName();
252 
253  // Build a <project GUID>, from the board name
254  wxString guid = GbrMakeProjectGUIDfromString( msg );
255 
256  // build the <project id> string: this is the board short filename (without ext)
257  // and all non ASCII chars are replaced by '_', to be compatible with .gbr files.
258  msg = fn.GetName();
259 
260  // build the <rec> string. All non ASCII chars and comma are replaced by '_'
261  wxString rev = ExpandTextVars( m_pcb->GetTitleBlock().GetRevision(), m_pcb->GetProject() );
262 
263  if( rev.IsEmpty() )
264  rev = wxT( "rev?" );
265 
266  m_json["GeneralSpecs"]["ProjectId"]["Name"] = msg.ToAscii();
267  m_json["GeneralSpecs"]["ProjectId"]["GUID"] = guid;
268  m_json["GeneralSpecs"]["ProjectId"]["Revision"] = rev.ToAscii();
269 
270  // output the board size in mm:
272 
273  m_json["GeneralSpecs"]["Size"]["X"] = mapValue( brect.GetWidth() );
274  m_json["GeneralSpecs"]["Size"]["Y"] = mapValue( brect.GetHeight() );
275 
276 
277  // Add some data to the JSON header, GeneralSpecs:
278  // number of copper layers
279  m_json["GeneralSpecs"]["LayerNumber"] = m_pcb->GetCopperLayerCount();
280 
281  // Board thickness
282  m_json["GeneralSpecs"]["BoardThickness"] =
284 
285  // Copper finish
287 
288  if( !brd_stackup.m_FinishType.IsEmpty() )
289  m_json["GeneralSpecs"]["Finish"] = brd_stackup.m_FinishType;
290 
291  if( brd_stackup.m_HasDielectricConstrains )
292  m_json["GeneralSpecs"]["ImpedanceControlled"] = true;
293 
294  if( brd_stackup.m_CastellatedPads )
295  m_json["GeneralSpecs"]["Castellated"] = true;
296 
297  if( brd_stackup.m_EdgePlating )
298  m_json["GeneralSpecs"]["EdgePlating"] = true;
299 
300  if( brd_stackup.m_EdgeConnectorConstraints )
301  {
302  m_json["GeneralSpecs"]["EdgeConnector"] = true;
303 
304  m_json["GeneralSpecs"]["EdgeConnectorBevelled"] =
305  ( brd_stackup.m_EdgeConnectorConstraints == BS_EDGE_CONNECTOR_BEVELLED );
306  }
307 
308 #if 0 // Not yet in use
309  /* The board type according to IPC-2221. There are six primary board types:
310  - Type 1 - Single-sided
311  - Type 2 - Double-sided
312  - Type 3 - Multilayer, TH components only
313  - Type 4 - Multilayer, with TH, blind and/or buried vias.
314  - Type 5 - Multilayer metal-core board, TH components only
315  - Type 6 - Multilayer metal-core
316  */
317  m_json["GeneralSpecs"]["IPC-2221-Type"] = 4;
318 
319  /* Via protection: key words:
320  Ia Tented - Single-sided
321  Ib Tented - Double-sided
322  IIa Tented and Covered - Single-sided
323  IIb Tented and Covered - Double-sided
324  IIIa Plugged - Single-sided
325  IIIb Plugged - Double-sided
326  IVa Plugged and Covered - Single-sided
327  IVb Plugged and Covered - Double-sided
328  V Filled (fully plugged)
329  VI Filled and Covered
330  VIII Filled and Capped
331  None...No protection
332  */
333  m_json["GeneralSpecs"]["ViaProtection"] = "Ib";
334 #endif
335 }
336 
337 
339 {
340  // Add the Files Attributes section in JSON format to m_JSONbuffer
341  m_json["FilesAttributes"] = nlohmann::ordered_json::array();
342 
343  for( unsigned ii = 0; ii < m_params.m_GerberFileList.GetCount(); ii++ )
344  {
345  wxString& name = m_params.m_GerberFileList[ii];
346  PCB_LAYER_ID layer = m_params.m_LayerId[ii];
347  wxString gbr_layer_id;
348  bool skip_file = false; // true to skip files which should not be in job file
349  const char* polarity = "Positive";
350 
351  nlohmann::ordered_json file_json;
352 
353  if( layer <= B_Cu )
354  {
355  gbr_layer_id = wxT( "Copper,L" );
356 
357  if( layer == B_Cu )
358  gbr_layer_id << m_pcb->GetCopperLayerCount();
359  else
360  gbr_layer_id << layer + 1;
361 
362  gbr_layer_id << wxT( "," );
363 
364  if( layer == B_Cu )
365  gbr_layer_id << wxT( "Bot" );
366  else if( layer == F_Cu )
367  gbr_layer_id << wxT( "Top" );
368  else
369  gbr_layer_id << wxT( "Inr" );
370  }
371 
372  else
373  {
374  switch( layer )
375  {
376  case B_Adhes:
377  gbr_layer_id = wxT( "Glue,Bot" );
378  break;
379  case F_Adhes:
380  gbr_layer_id = wxT( "Glue,Top" );
381  break;
382 
383  case B_Paste:
384  gbr_layer_id = wxT( "SolderPaste,Bot" );
385  break;
386  case F_Paste:
387  gbr_layer_id = wxT( "SolderPaste,Top" );
388  break;
389 
390  case B_SilkS:
391  gbr_layer_id = wxT( "Legend,Bot" );
392  break;
393  case F_SilkS:
394  gbr_layer_id = wxT( "Legend,Top" );
395  break;
396 
397  case B_Mask:
398  gbr_layer_id = wxT( "SolderMask,Bot" );
399  polarity = "Negative";
400  break;
401  case F_Mask:
402  gbr_layer_id = wxT( "SolderMask,Top" );
403  polarity = "Negative";
404  break;
405 
406  case Edge_Cuts:
407  gbr_layer_id = wxT( "Profile" );
408  break;
409 
410  case B_Fab:
411  gbr_layer_id = wxT( "AssemblyDrawing,Bot" );
412  break;
413  case F_Fab:
414  gbr_layer_id = wxT( "AssemblyDrawing,Top" );
415  break;
416 
417  case Margin:
418  case B_CrtYd:
419  case F_CrtYd:
420  skip_file = true;
421  break;
422 
423  case Dwgs_User:
424  case Cmts_User:
425  case Eco1_User:
426  case Eco2_User:
427  case User_1:
428  case User_2:
429  case User_3:
430  case User_4:
431  case User_5:
432  case User_6:
433  case User_7:
434  case User_8:
435  case User_9:
436  gbr_layer_id = wxT( "Other,User" );
437  break;
438 
439  default:
440  skip_file = true;
441 
442  if( m_reporter )
443  m_reporter->Report( wxT( "Unexpected layer id in job file" ), RPT_SEVERITY_ERROR );
444 
445  break;
446  }
447  }
448 
449  if( !skip_file )
450  {
451  // name can contain non ASCII7 chars.
452  // Ensure the name is JSON compatible.
453  std::string strname = formatStringFromUTF32( name );
454 
455  file_json["Path"] = strname.c_str();
456  file_json["FileFunction"] = gbr_layer_id;
457  file_json["FilePolarity"] = polarity;
458 
459  m_json["FilesAttributes"] += file_json;
460  }
461  }
462 }
463 
464 
466 {
467  // Add the Design Rules section in JSON format to m_JSONbuffer
468  // Job file support a few design rules:
469  const BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings();
470  NETCLASS defaultNC = *dsnSettings.GetDefault();
471  int minclearanceOuter = defaultNC.GetClearance();
472  bool hasInnerLayers = m_pcb->GetCopperLayerCount() > 2;
473 
474  // Search a smaller clearance in other net classes, if any.
475  for( const std::pair<const wxString, NETCLASSPTR>& entry : dsnSettings.GetNetClasses() )
476  minclearanceOuter = std::min( minclearanceOuter, entry.second->GetClearance() );
477 
478  // job file knows different clearance types.
479  // Kicad knows only one clearance for pads and tracks
480  int minclearance_track2track = minclearanceOuter;
481 
482  // However, pads can have a specific clearance defined for a pad or a footprint,
483  // and min clearance can be dependent on layers.
484  // Search for a minimal pad clearance:
485  int minPadClearanceOuter = defaultNC.GetClearance();
486  int minPadClearanceInner = defaultNC.GetClearance();
487 
488  for( FOOTPRINT* footprint : m_pcb->Footprints() )
489  {
490  for( PAD* pad : footprint->Pads() )
491  {
492  for( PCB_LAYER_ID layer : pad->GetLayerSet().Seq() )
493  {
494  int padClearance = pad->GetOwnClearance( layer );
495 
496  if( layer == B_Cu || layer == F_Cu )
497  minPadClearanceOuter = std::min( minPadClearanceOuter, padClearance );
498  else
499  minPadClearanceInner = std::min( minPadClearanceInner, padClearance );
500  }
501  }
502  }
503 
504  m_json["DesignRules"] = { {
505  { "Layers", "Outer" },
506  { "PadToPad", mapValue( minPadClearanceOuter ) },
507  { "PadToTrack", mapValue( minPadClearanceOuter ) },
508  { "TrackToTrack", mapValue( minclearance_track2track ) }
509  } };
510 
511  // Until this is changed in Kicad, use the same value for internal tracks
512  int minclearanceInner = minclearanceOuter;
513 
514  // Output the minimal track width
515  int mintrackWidthOuter = INT_MAX;
516  int mintrackWidthInner = INT_MAX;
517 
518  for( PCB_TRACK* track : m_pcb->Tracks() )
519  {
520  if( track->Type() == PCB_VIA_T )
521  continue;
522 
523  if( track->GetLayer() == B_Cu || track->GetLayer() == F_Cu )
524  mintrackWidthOuter = std::min( mintrackWidthOuter, track->GetWidth() );
525  else
526  mintrackWidthInner = std::min( mintrackWidthInner, track->GetWidth() );
527  }
528 
529  if( mintrackWidthOuter != INT_MAX )
530  m_json["DesignRules"][0]["MinLineWidth"] = mapValue( mintrackWidthOuter );
531 
532  // Output the minimal zone to xx clearance
533  // Note: zones can have a zone clearance set to 0
534  // if happens, the actual zone clearance is the clearance of its class
535  minclearanceOuter = INT_MAX;
536  minclearanceInner = INT_MAX;
537 
538  for( ZONE* zone : m_pcb->Zones() )
539  {
540  if( zone->GetIsRuleArea() || !zone->IsOnCopperLayer() )
541  continue;
542 
543  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
544  {
545  int zclerance = zone->GetOwnClearance( layer );
546 
547  if( layer == B_Cu || layer == F_Cu )
548  minclearanceOuter = std::min( minclearanceOuter, zclerance );
549  else
550  minclearanceInner = std::min( minclearanceInner, zclerance );
551  }
552  }
553 
554  if( minclearanceOuter != INT_MAX )
555  m_json["DesignRules"][0]["TrackToRegion"] = mapValue( minclearanceOuter );
556 
557  if( minclearanceOuter != INT_MAX )
558  m_json["DesignRules"][0]["RegionToRegion"] = mapValue( minclearanceOuter );
559 
560  if( hasInnerLayers )
561  {
562  m_json["DesignRules"] += nlohmann::ordered_json( {
563  { "Layers", "Inner" },
564  { "PadToPad", mapValue( minPadClearanceInner ) },
565  { "PadToTrack", mapValue( minPadClearanceInner ) },
566  { "TrackToTrack", mapValue( minclearance_track2track ) }
567  } );
568 
569  if( mintrackWidthInner != INT_MAX )
570  m_json["DesignRules"][1]["MinLineWidth"] = mapValue( mintrackWidthInner );
571 
572  if( minclearanceInner != INT_MAX )
573  m_json["DesignRules"][1]["TrackToRegion"] = mapValue( minclearanceInner );
574 
575  if( minclearanceInner != INT_MAX )
576  m_json["DesignRules"][1]["RegionToRegion"] = mapValue( minclearanceInner );
577  }
578 }
579 
580 
582 {
583  // Add the Material Stackup section in JSON format to m_JSONbuffer
584  m_json["MaterialStackup"] = nlohmann::ordered_json::array();
585 
586  // Build the candidates list:
587  LSET maskLayer;
589 
590  // Ensure brd_stackup is up to date (i.e. no change made by SynchronizeWithBoard() )
591  bool uptodate = not brd_stackup.SynchronizeWithBoard( &m_pcb->GetDesignSettings() );
592 
593  if( m_reporter && !uptodate && m_pcb->GetDesignSettings().m_HasStackup )
594  m_reporter->Report( _( "Board stackup settings not up to date." ), RPT_SEVERITY_ERROR );
595 
596  PCB_LAYER_ID last_copper_layer = F_Cu;
597 
598  // Generate the list (top to bottom):
599  for( int ii = 0; ii < brd_stackup.GetCount(); ++ii )
600  {
601  BOARD_STACKUP_ITEM* item = brd_stackup.GetStackupLayer( ii );
602 
603  int sub_layer_count =
604  item->GetType() == BS_ITEM_TYPE_DIELECTRIC ? item->GetSublayersCount() : 1;
605 
606  for( int sub_idx = 0; sub_idx < sub_layer_count; sub_idx++ )
607  {
608  // layer thickness is always in mm
609  double thickness = mapValue( item->GetThickness( sub_idx ) );
610  wxString layer_type;
611  std::string layer_name; // for comment
612 
613  nlohmann::ordered_json layer_json;
614 
615  switch( item->GetType() )
616  {
617  case BS_ITEM_TYPE_COPPER:
618  layer_type = wxT( "Copper" );
619  layer_name = formatStringFromUTF32( m_pcb->GetLayerName( item->GetBrdLayerId() ) );
620  last_copper_layer = item->GetBrdLayerId();
621  break;
622 
624  layer_type = wxT( "Legend" );
625  layer_name = formatStringFromUTF32( item->GetTypeName() );
626  break;
627 
629  layer_type = wxT( "SolderMask" );
630  layer_name = formatStringFromUTF32( item->GetTypeName() );
631  break;
632 
634  layer_type = wxT( "SolderPaste" );
635  layer_name = formatStringFromUTF32( item->GetTypeName() );
636  break;
637 
639  layer_type = wxT( "Dielectric" );
640  // The option core or prepreg is not added here, as it creates constraints
641  // in build process, not necessary wanted.
642  if( sub_layer_count > 1 )
643  {
644  layer_name =
645  formatStringFromUTF32( wxString::Format( wxT( "dielectric layer %d - %d/%d" ),
646  item->GetDielectricLayerId(), sub_idx + 1, sub_layer_count ) );
647  }
648  else
650  wxT( "dielectric layer %d" ), item->GetDielectricLayerId() ) );
651  break;
652 
653  default:
654  break;
655  }
656 
657  layer_json["Type"] = layer_type;
658 
659  if( item->IsColorEditable() && uptodate )
660  {
661  if( IsPrmSpecified( item->GetColor() ) )
662  {
663  wxString colorName = item->GetColor();
664 
665  if( colorName.StartsWith( wxT( "#" ) ) ) // This is a user defined color.
666  {
667  // In job file a color can be given by its RGB values (0...255)
668  wxColor color( COLOR4D( colorName ).ToColour() );
669  colorName.Printf( wxT( "R%dG%dB%d" ),
670  color.Red(),
671  color.Green(),
672  color.Blue() );
673  }
674 
675  layer_json["Color"] = colorName;
676  }
677  }
678 
679  if( item->IsThicknessEditable() && uptodate )
680  layer_json["Thickness"] = thickness;
681 
682  if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
683  {
684  if( item->HasMaterialValue() )
685  {
686  layer_json["Material"] = item->GetMaterial( sub_idx );
687 
688  // These constrains are only written if the board has impedance controlled tracks.
689  // If the board is not impedance controlled, they are useless.
690  // Do not add constrains that create more expensive boards.
691 
692  if( brd_stackup.m_HasDielectricConstrains )
693  {
694  // Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not
695  // a possible value
696  if( item->GetEpsilonR() > 1.0 )
697  layer_json["DielectricConstant"] = item->FormatEpsilonR( sub_idx );
698 
699  // Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not
700  // a possible value
701  if( item->GetLossTangent() > 0.0 )
702  layer_json["LossTangent"] = item->FormatLossTangent( sub_idx );
703  }
704  }
705 
706  PCB_LAYER_ID next_copper_layer = ( PCB_LAYER_ID )( last_copper_layer + 1 );
707 
708  // If the next_copper_layer is the last copper layer, the next layer id is B_Cu
709  if( next_copper_layer >= m_pcb->GetCopperLayerCount() - 1 )
710  next_copper_layer = B_Cu;
711 
712  wxString subLayerName;
713 
714  if( sub_layer_count > 1 )
715  subLayerName.Printf( wxT( " (%d/%d)" ), sub_idx + 1, sub_layer_count );
716 
717  wxString name = wxString::Format( wxT( "%s/%s%s" ),
718  formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ),
719  formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ),
720  subLayerName );
721 
722  layer_json["Name"] = name;
723 
724  // Add a comment ("Notes"):
725  wxString note;
726 
727  note << wxString::Format( wxT( "Type: %s" ), layer_name.c_str() );
728 
729  note << wxString::Format( wxT( " (from %s to %s)" ),
730  formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ),
731  formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) );
732 
733  layer_json["Notes"] = note;
734  }
735  else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK
736  || item->GetType() == BS_ITEM_TYPE_SILKSCREEN )
737  {
738  if( item->HasMaterialValue() )
739  {
740  layer_json["Material"] = item->GetMaterial();
741 
742  // These constrains are only written if the board has impedance controlled tracks.
743  // If the board is not impedance controlled, they are useless.
744  // Do not add constrains that create more expensive boards.
745  if( brd_stackup.m_HasDielectricConstrains )
746  {
747  // Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not
748  // a possible value
749  if( item->GetEpsilonR() > 1.0 )
750  layer_json["DielectricConstant"] = item->FormatEpsilonR();
751 
752  // Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not
753  // a possible value
754  if( item->GetLossTangent() > 0.0 )
755  layer_json["LossTangent"] = item->FormatLossTangent();
756  }
757  }
758 
759  layer_json["Name"] = layer_name.c_str();
760  }
761  else
762  {
763  layer_json["Name"] = layer_name.c_str();
764  }
765 
766  m_json["MaterialStackup"].insert( m_json["MaterialStackup"].end(), layer_json );
767  }
768  }
769 }
BOARD_STACKUP_ITEM_TYPE GetType() const
nlohmann::ordered_json m_json
std::string formatStringFromUTF32(const wxString &aText)
A helper function to convert a wxString ( therefore a Unicode text ) to a JSON compatible string (a e...
Handle special data (items attributes) during plot.
Plot settings, and plotting engines (PostScript, Gerber, HPGL and DXF)
BOARD_STACKUP_ITEM * GetStackupLayer(int aIndex)
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:362
ZONES & Zones()
Definition: board.h:240
bool HasMaterialValue(int aDielectricSubLayer=0) const
double mapValue(double aUiValue)
A helper function to convert a double in Pcbnew internal units to a JSON double value (in mm),...
bool CreateJobFile(const wxString &aFullFilename)
Creates a Gerber job file.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:57
Manage layers needed to make a physical board.
int GetSublayersCount() const
static constexpr double IU_PER_MM
Mock up a conversion function.
const EDA_RECT GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:738
void addJSONHeader()
Add the job file header in JSON format to m_JSONbuffer.
int color
Definition: DXF_plotter.cpp:57
int GetWidth() const
Definition: eda_rect.h:118
const char * sideKeyValue(enum ONSIDE aValue)
PROJECT * GetProject() const
Definition: board.h:361
wxString GetColor() const
wxString GbrMakeCreationDateAttributeString(GBR_NC_STRING_FORMAT aFormat)
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:70
const wxString & GetFileName() const
Definition: board.h:229
Classes used to generate a Gerber job file in JSON.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
void addJSONFilesAttributes()
Add the Files Attributes section in JSON format to m_JSONbuffer.
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
double GetLossTangent(int aDielectricSubLayer=0) const
bool SynchronizeWithBoard(BOARD_DESIGN_SETTINGS *aSettings)
Synchronize the BOARD_STACKUP_ITEM* list with the board.
Board plot function definition file.
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
wxArrayString m_GerberFileList
bool m_HasDielectricConstrains
True if some layers have impedance controlled tracks or have specific constrains for micro-wave appli...
BOARD_STACKUP & GetStackupDescriptor()
bool IsThicknessEditable() const
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:516
wxString GetBuildVersion()
Get the full KiCad version string.
const wxString & GetRevision() const
Definition: title_block.h:86
int GetThickness(int aDielectricSubLayer=0) const
bool IsColorEditable() const
wxString GbrMakeProjectGUIDfromString(const wxString &aText)
Build a project GUID using format RFC4122 Version 1 or 4 from the project name, because a KiCad proje...
FOOTPRINTS & Footprints()
Definition: board.h:234
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:46
double GetEpsilonR(int aDielectricSubLayer=0) const
wxString FormatEpsilonR(int aDielectricSubLayer=0) const
Definition of file extensions used in Kicad.
wxString GetTypeName() const
#define _(s)
wxString FormatLossTangent(int aDielectricSubLayer=0) const
NETCLASSES & GetNetClasses() const
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
int GetCount() const
Manage one layer needed to make a physical board.
Definition: board_stackup.h:89
int GetHeight() const
Definition: eda_rect.h:119
bool WriteJSONJobFile(const wxString &aFullFilename)
Creates an Gerber job file in JSON format.
PCB_LAYER_ID GetBrdLayerId() const
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
TITLE_BLOCK & GetTitleBlock()
Definition: board.h:542
bool IsPrmSpecified(const wxString &aPrmValue)
const char * name
Definition: DXF_plotter.cpp:56
int GetDielectricLayerId() const
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
int GetClearance() const
Definition: netclass.h:124
Definition: layer_ids.h:71
void addJSONDesignRules()
Add the Design Rules section in JSON format to m_JSONbuffer.
int GetCopperLayerCount() const
Definition: board.cpp:455
Handle the component boundary box.
Definition: eda_rect.h:42
NETCLASS * GetDefault() const
std::vector< PCB_LAYER_ID > m_LayerId
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
Definition: pad.h:57
void addJSONMaterialStackup()
Add the Material Stackup section in JSON format to m_JSONbuffer This is the ordered list of stackup l...
wxString GetMaterial(int aDielectricSubLayer=0) const
void addJSONGeneralSpecs()
Add the General Specs in JSON format to m_JSONbuffer.
TRACKS & Tracks()
Definition: board.h:231
GERBER_JOBFILE_WRITER(BOARD *aPcb, REPORTER *aReporter=nullptr)
Container for design settings for a BOARD object.
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103