KiCad PCB EDA Suite
rs274x.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) 2007-2018 Jean-Pierre Charras jp.charras at wanadoo.fr
5  * Copyright (C) 1992-2018 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 #include <base_units.h>
30 #include <math/util.h> // for KiROUND
31 
32 #include <gerbview.h>
33 #include <gerber_file_image.h>
34 #include <ignore.h>
35 #include <macros.h>
36 #include <X2_gerber_attributes.h>
37 #include <gbr_metadata.h>
38 
39 extern int ReadInt( char*& text, bool aSkipSeparator = true );
40 extern double ReadDouble( char*& text, bool aSkipSeparator = true );
41 
42 
43 #define CODE( x, y ) ( ( (x) << 8 ) + (y) )
44 
45 // See rs274xrevd_e.pdf, table 1: RS-274X parameters order of entry
46 // in gerber files, when a coordinate is given (like X78Y600 or I0J80):
47 // Y and Y are logical coordinates
48 // A and B are plotter coordinates
49 // Usually A = X, B = Y
50 // But we can have A = Y, B = X and/or offset, mirror, scale;
51 // Also:
52 // Image is what you must plot (the entire data of the file).
53 // Layer is just a set of data blocks with their parameters. An image can have more than one
54 // layer so a gerber layer is not like a board layer or the graphic layers used in GerbView
55 // to show a file.
57  // Directive parameters: single usage recommended
58  // Must be at the beginning of the file
59  AXIS_SELECT = CODE( 'A', 'S' ), // Default: A=X, B=Y
60  FORMAT_STATEMENT = CODE( 'F', 'S' ), // no default: this command must exists
61  MIRROR_IMAGE = CODE( 'M', 'I' ), // Default: mo mirror
62  MODE_OF_UNITS = CODE( 'M', 'O' ), // Default: inch
63  INCH = CODE( 'I', 'N' ),
64  MILLIMETER = CODE( 'M', 'M' ),
65  OFFSET = CODE( 'O', 'F' ), // Default: A = 0, B = 0
66  SCALE_FACTOR = CODE( 'S', 'F' ), // Default: A = 1.0, B = 1.0
67 
68  // Image parameters:
69  // commands used only once at the beginning of the file, and are deprecated
70  IMAGE_JUSTIFY = CODE( 'I', 'J' ), // Default: no justification
71  IMAGE_NAME = CODE( 'I', 'N' ), // Default: void
72  IMAGE_OFFSET = CODE( 'I', 'O' ), // Default: A = 0, B = 0
73  IMAGE_POLARITY = CODE( 'I', 'P' ), // Default: Positive
74  IMAGE_ROTATION = CODE( 'I', 'R' ), // Default: 0
75 
76  // Aperture parameters:
77  // Usually for the whole file
78  AP_DEFINITION = CODE( 'A', 'D' ),
79  AP_MACRO = CODE( 'A', 'M' ),
80 
81  // X2 extension attribute commands
82  // Mainly are found standard attributes and user attributes
83  // standard attributes commands are:
84  // TF (file attribute) TO (net attribute)
85  // TA (aperture attribute) and TD (delete aperture attribute)
86  FILE_ATTRIBUTE = CODE( 'T', 'F' ),
87 
88  // X2 extension Net attribute info
89  // Net attribute options are:
90  // TO (net attribute data): TO.CN or TO.P TO.N or TO.C
91  NET_ATTRIBUTE = CODE( 'T', 'O' ),
92 
93  // X2 extension Aperture attribute TA
94  APERTURE_ATTRIBUTE = CODE( 'T', 'A' ),
95 
96  // TD (delete aperture/object attribute):
97  // Delete aperture attribute added by %TA or Oblect attribute added b %TO
98  // TD (delete all) or %TD<attr name> to delete <attr name>.
99  // eg: TD.P or TD.N or TD.C ...
101 
102  // Layer specific parameters
103  // May be used singly or may be layer specific
104  // These parameters are at the beginning of the file or layer
105  // and reset some layer parameters (like interpolation)
106  KNOCKOUT = CODE( 'K', 'O' ), // Default: off
107  STEP_AND_REPEAT = CODE( 'S', 'R' ), // Default: A = 1, B = 1
108  ROTATE = CODE( 'R', 'O' ), // Default: 0
109 
110  LOAD_POLARITY = CODE( 'L', 'P' ), //LPC or LPD. Default: Dark (LPD)
111  LOAD_NAME = CODE( 'L', 'N' ), // Deprecated: equivalent to G04
112 };
113 
114 
116 {
117  /* reads two bytes of data and assembles them into an int with the first
118  * byte in the sequence put into the most significant part of a 16 bit value
119  */
120  int result;
121  int currbyte;
122 
123  if( text && *text )
124  {
125  currbyte = *text++;
126  result = ( currbyte & 0xFF ) << 8;
127  }
128  else
129  return -1;
130 
131  if( text && *text )
132  {
133  currbyte = *text++;
134  result += currbyte & 0xFF;
135  }
136  else
137  return -1;
138 
139  return result;
140 }
141 
142 
143 bool GERBER_FILE_IMAGE::ReadRS274XCommand( char *aBuff, unsigned int aBuffSize, char*& aText )
144 {
145  bool ok = true;
146  int code_command;
147 
148  aText++;
149 
150  for( ; ; )
151  {
152  while( *aText )
153  {
154  switch( *aText )
155  {
156  case '%': // end of command
157  aText++;
159  goto exit; // success completion
160 
161  case ' ':
162  case '\r':
163  case '\n':
164  aText++;
165  break;
166 
167  case '*':
168  aText++;
169  break;
170 
171  default:
172  code_command = ReadXCommandID( aText );
173  ok = ExecuteRS274XCommand( code_command, aBuff, aBuffSize, aText );
174 
175  if( !ok )
176  goto exit;
177 
178  break;
179  }
180  }
181 
182  // end of current line, read another one.
183  if( fgets( aBuff, aBuffSize, m_Current_File ) == nullptr )
184  {
185  // end of file
186  ok = false;
187  break;
188  }
189  m_LineNum++;
190  aText = aBuff;
191  }
192 
193 exit:
194  return ok;
195 }
196 
197 
198 bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff,
199  unsigned int aBuffSize, char*& aText )
200 {
201  int code;
202  int seq_len; // not used, just provided
203  int seq_char;
204  bool ok = true;
205  wxString msg;
206  double fcoord;
207  bool x_fmt_known = false;
208  bool y_fmt_known = false;
209 
210  // conv_scale = scaling factor from inch to Internal Unit
211  double conv_scale = IU_PER_MILS * 1000;
212 
213  if( m_GerbMetric )
214  conv_scale /= 25.4;
215 
216  switch( aCommand )
217  {
218  case FORMAT_STATEMENT:
219  seq_len = 2;
220 
221  while( *aText != '*' )
222  {
223  switch( *aText )
224  {
225  case ' ':
226  aText++;
227  break;
228 
229  case 'D': // Non-standard option for all zeros (leading + tailing)
230  msg.Printf( _( "RS274X: Invalid GERBER format command '%c' at line %d: \"%s\"" ),
231  'D', m_LineNum, aBuff );
232  AddMessageToList( msg );
233  msg.Printf( _("GERBER file \"%s\" may not display as intended." ),
234  m_FileName.ToAscii() );
235  AddMessageToList( msg );
237 
238  case 'L': // No Leading 0
239  m_NoTrailingZeros = false;
240  aText++;
241  break;
242 
243  case 'T': // No trailing 0
244  m_NoTrailingZeros = true;
245  aText++;
246  break;
247 
248  case 'A': // Absolute coord
249  m_Relative = false;
250  aText++;
251  break;
252 
253  case 'I': // Relative coord
254  m_Relative = true;
255  aText++;
256  break;
257 
258  case 'G':
259  case 'N': // Sequence code (followed by one digit: the sequence len)
260  // (sometimes found before the X,Y sequence)
261  // Obscure option
262  aText++;
263  seq_char = *aText++;
264 
265  if( (seq_char >= '0') && (seq_char <= '9') )
266  seq_len = seq_char - '0';
267 
268  break;
269 
270  case 'M': // Sequence code (followed by one digit: the sequence len)
271  // (sometimes found after the X,Y sequence)
272  // Obscure option
273  aText++;
274  code = *aText;
275 
276  if( ( code >= '0' ) && ( code <= '9' ) )
277  aText++; // skip the digit
278 
279  break;
280 
281  case 'X':
282  case 'Y':
283  {
284  code = *(aText++);
285  char ctmp = *(aText++) - '0';
286 
287  if( code == 'X' )
288  {
289  x_fmt_known = true;
290  // number of digits after the decimal point (0 to 7 allowed)
291  m_FmtScale.x = *aText - '0';
292  m_FmtLen.x = ctmp + m_FmtScale.x;
293 
294  // m_FmtScale is 0 to 7
295  // (Old Gerber specification was 0 to 6)
296  if( m_FmtScale.x < 0 )
297  m_FmtScale.x = 0;
298 
299  if( m_FmtScale.x > 7 )
300  m_FmtScale.x = 7;
301  }
302  else
303  {
304  y_fmt_known = true;
305  m_FmtScale.y = *aText - '0';
306  m_FmtLen.y = ctmp + m_FmtScale.y;
307 
308  if( m_FmtScale.y < 0 )
309  m_FmtScale.y = 0;
310 
311  if( m_FmtScale.y > 7 )
312  m_FmtScale.y = 7;
313  }
314 
315  aText++;
316  }
317  break;
318 
319  case '*':
320  break;
321 
322  default:
323  msg.Printf( wxT( "Unknown id (%c) in FS command" ),
324  *aText );
325  AddMessageToList( msg );
326  GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
327  ok = false;
328  break;
329  }
330  }
331 
332  if( !x_fmt_known || !y_fmt_known )
333  AddMessageToList( wxT( "RS274X: Format Statement (FS) without X or Y format" ) );
334 
335  break;
336 
337  case AXIS_SELECT: // command ASAXBY*% or %ASAYBX*%
338  m_SwapAxis = false;
339 
340  if( strncasecmp( aText, "AYBX", 4 ) == 0 )
341  m_SwapAxis = true;
342 
343  break;
344 
345  case MIRROR_IMAGE: // command %MIA0B0*%, %MIA0B1*%, %MIA1B0*%, %MIA1B1*%
346  m_MirrorA = m_MirrorB = false;
347 
348  while( *aText && *aText != '*' )
349  {
350  switch( *aText )
351  {
352  case 'A': // Mirror A axis ?
353  aText++;
354 
355  if( *aText == '1' )
356  m_MirrorA = true;
357 
358  break;
359 
360  case 'B': // Mirror B axis ?
361  aText++;
362 
363  if( *aText == '1' )
364  m_MirrorB = true;
365 
366  break;
367 
368  default:
369  aText++;
370  break;
371  }
372  }
373  break;
374 
375  case MODE_OF_UNITS:
376  code = ReadXCommandID( aText );
377 
378  if( code == INCH )
379  m_GerbMetric = false;
380  else if( code == MILLIMETER )
381  m_GerbMetric = true;
382 
383  conv_scale = m_GerbMetric ? IU_PER_MILS / 25.4 : IU_PER_MILS;
384  break;
385 
386  case FILE_ATTRIBUTE: // Command %TF ...
387  {
389  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
390 
391  if( dummy.IsFileFunction() )
392  {
393  delete m_FileFunction;
395 
396  // Don't set this until we get a file function; other code expects m_IsX2_file == true
397  // to mean that we have a valid m_FileFunction
398  m_IsX2_file = true;
399  }
400  else if( dummy.IsFileMD5() )
401  {
402  m_MD5_value = dummy.GetPrm( 1 );
403  }
404  else if( dummy.IsFilePart() )
405  {
406  m_PartString = dummy.GetPrm( 1 );
407  }
408  }
409  break;
410 
411  case APERTURE_ATTRIBUTE: // Command %TA
412  {
414  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
415 
416  if( dummy.GetAttribute() == ".AperFunction" )
417  {
418  m_AperFunction = dummy.GetPrm( 1 );
419 
420  // A few function values can have other parameters. Add them
421  for( int ii = 2; ii < dummy.GetPrmCount(); ii++ )
422  m_AperFunction << "," << dummy.GetPrm( ii );
423  }
424  }
425  break;
426 
427  case NET_ATTRIBUTE: // Command %TO currently %TO.P %TO.N and %TO.C
428  {
430 
431  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
432 
433  if( dummy.GetAttribute() == ".N" )
434  {
437  }
438  else if( dummy.GetAttribute() == ".C" )
439  {
442  }
443  else if( dummy.GetAttribute() == ".P" )
444  {
447  m_NetAttributeDict.m_Padname.SetField( FormatStringFromGerber( dummy.GetPrm( 2 ) ), true, true );
448 
449  if( dummy.GetPrmCount() > 3 )
450  {
452  FormatStringFromGerber( dummy.GetPrm( 3 ) ), true, true );
453  }
454  else
455  {
457  }
458  }
459  }
460  break;
461 
462  case REMOVE_APERTURE_ATTRIBUTE: // Command %TD ...
463  {
465  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
467  }
468  break;
469 
470  case OFFSET: // command: OFAnnBnn (nn = float number) = layer Offset
471  m_Offset.x = m_Offset.y = 0;
472  while( *aText != '*' )
473  {
474  switch( *aText )
475  {
476  case 'A': // A axis offset in current unit (inch or mm)
477  aText++;
478  fcoord = ReadDouble( aText );
479  m_Offset.x = KiROUND( fcoord * conv_scale );
480  break;
481 
482  case 'B': // B axis offset in current unit (inch or mm)
483  aText++;
484  fcoord = ReadDouble( aText );
485  m_Offset.y = KiROUND( fcoord * conv_scale );
486  break;
487  }
488  }
489  break;
490 
491  case SCALE_FACTOR:
492  m_Scale.x = m_Scale.y = 1.0;
493  while( *aText != '*' )
494  {
495  switch( *aText )
496  {
497  case 'A': // A axis scale
498  aText++;
499  m_Scale.x = ReadDouble( aText );
500  break;
501 
502  case 'B': // B axis scale
503  aText++;
504  m_Scale.y = ReadDouble( aText );
505  break;
506  }
507  }
508  break;
509 
510  case IMAGE_OFFSET: // command: IOAnnBnn (nn = float number) = Image Offset
511  m_ImageOffset.x = m_ImageOffset.y = 0;
512  while( *aText != '*' )
513  {
514  switch( *aText )
515  {
516  case 'A': // A axis offset in current unit (inch or mm)
517  aText++;
518  fcoord = ReadDouble( aText );
519  m_ImageOffset.x = KiROUND( fcoord * conv_scale );
520  break;
521 
522  case 'B': // B axis offset in current unit (inch or mm)
523  aText++;
524  fcoord = ReadDouble( aText );
525  m_ImageOffset.y = KiROUND( fcoord * conv_scale );
526  break;
527  }
528  }
529  break;
530 
531  case IMAGE_ROTATION: // command IR0* or IR90* or IR180* or IR270*
532  if( strncasecmp( aText, "0*", 2 ) == 0 )
533  m_ImageRotation = 0;
534  else if( strncasecmp( aText, "90*", 3 ) == 0 )
535  m_ImageRotation = 90;
536  else if( strncasecmp( aText, "180*", 4 ) == 0 )
537  m_ImageRotation = 180;
538  else if( strncasecmp( aText, "270*", 4 ) == 0 )
539  m_ImageRotation = 270;
540  else
541  AddMessageToList( _( "RS274X: Command \"IR\" rotation value not allowed" ) );
542 
543  break;
544 
545  case STEP_AND_REPEAT: // command SR, like %SRX3Y2I5.0J2*%
546  m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
548  GetLayerParams().m_StepForRepeat.x = 0.0; // offset for Step and Repeat command
550  GetLayerParams().m_YRepeatCount = 1; // The repeat count
551  GetLayerParams().m_StepForRepeatMetric = m_GerbMetric; // the step units
552 
553  while( *aText && *aText != '*' )
554  {
555  switch( *aText )
556  {
557  case 'I': // X axis offset
558  aText++;
560  break;
561 
562  case 'J': // Y axis offset
563  aText++;
565  break;
566 
567  case 'X': // X axis repeat count
568  aText++;
570  break;
571 
572  case 'Y': // Y axis offset
573  aText++;
575  break;
576 
577  default:
578  aText++;
579  break;
580  }
581  }
582  break;
583 
584  case IMAGE_JUSTIFY: // Command IJAnBn*
585  m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false)
586  m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false)
587  m_ImageJustifyOffset = wxPoint(0,0); // Image Justify Offset on XY axis (default = 0,0)
588  while( *aText && *aText != '*' )
589  {
590  // IJ command is (for A or B axis) AC or AL or A<coordinate>
591  switch( *aText )
592  {
593  case 'A': // A axis justify
594  aText++;
595 
596  if( *aText == 'C' )
597  {
598  m_ImageJustifyXCenter = true;
599  aText++;
600  }
601  else if( *aText == 'L' )
602  {
603  m_ImageJustifyXCenter = true;
604  aText++;
605  }
606  else
607  {
608  m_ImageJustifyOffset.x = KiROUND( ReadDouble( aText ) * conv_scale);
609  }
610 
611  break;
612 
613  case 'B': // B axis justify
614  aText++;
615 
616  if( *aText == 'C' )
617  {
618  m_ImageJustifyYCenter = true;
619  aText++;
620  }
621  else if( *aText == 'L' )
622  {
623  m_ImageJustifyYCenter = true;
624  aText++;
625  }
626  else
627  {
628  m_ImageJustifyOffset.y = KiROUND( ReadDouble( aText ) * conv_scale);
629  }
630 
631  break;
632 
633  default:
634  aText++;
635  break;
636  }
637  }
638 
640  m_ImageJustifyOffset.x = 0;
641 
643  m_ImageJustifyOffset.y = 0;
644 
645  break;
646 
647  case KNOCKOUT:
648  m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
649  msg = _( "RS274X: Command KNOCKOUT ignored by GerbView" ) ;
650  AddMessageToList( msg );
651  break;
652 
653  case ROTATE: // Layer rotation: command like %RO45*%
654  m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
655  m_LocalRotation = ReadDouble( aText ); // Store layer rotation in degrees
656  break;
657 
658  case IMAGE_NAME:
659  m_ImageName.Empty();
660 
661  while( *aText != '*' )
662  m_ImageName.Append( *aText++ );
663 
664  break;
665 
666  case LOAD_NAME:
667  // %LN is a (deprecated) equivalentto G04: a comment
668  while( *aText && *aText != '*' )
669  aText++; // Skip text
670 
671  break;
672 
673  case IMAGE_POLARITY:
674  if( strncasecmp( aText, "NEG", 3 ) == 0 )
675  m_ImageNegative = true;
676  else
677  m_ImageNegative = false;
678 
679  break;
680 
681  case LOAD_POLARITY:
682  if( *aText == 'C' )
684  else
686 
687  break;
688 
689  case AP_MACRO: // lines like %AMMYMACRO*
690  // 5,1,8,0,0,1.08239X$1,22.5*
691  // %
692  /*ok = */ReadApertureMacro( aBuff, aBuffSize, aText, m_Current_File );
693  break;
694 
695  case AP_DEFINITION:
696  /* input example: %ADD30R,0.081800X0.101500*%
697  * Aperture definition has 4 options: C, R, O, P
698  * (Circle, Rect, Oval, regular Polygon)
699  * and shapes can have a hole (round or rectangular).
700  * All optional parameters values start by X
701  * at this point, text points to 2nd 'D'
702  */
703  if( *aText++ != 'D' )
704  {
705  ok = false;
706  break;
707  }
708 
709  m_Has_DCode = true;
710 
711  code = ReadInt( aText );
712 
713  D_CODE* dcode;
714  dcode = GetDCODEOrCreate( code );
715 
716  if( dcode == nullptr )
717  break;
718 
720 
721  // at this point, text points to character after the ADD<num>,
722  // i.e. R in example above. If aText[0] is one of the usual
723  // apertures: (C,R,O,P), there is a comma after it.
724  if( aText[1] == ',' )
725  {
726  char stdAperture = *aText;
727 
728  aText += 2; // skip "C," for example
729 
730  // First parameter is the size X:
731  dcode->m_Size.x = KiROUND( ReadDouble( aText ) * conv_scale );
732  dcode->m_Size.y = dcode->m_Size.x;
733 
734  switch( stdAperture ) // Aperture desceiption has optional parameters. Read them
735  {
736  case 'C': // Circle
737  dcode->m_Shape = APT_CIRCLE;
738  while( *aText == ' ' )
739  aText++;
740 
741  if( *aText == 'X' )
742  {
743  aText++;
744  dcode->m_Drill.x = dcode->m_Drill.y =
745  KiROUND( ReadDouble( aText ) * conv_scale );
747  }
748 
749  while( *aText == ' ' )
750  aText++;
751 
752  if( *aText == 'X' )
753  {
754  aText++;
755  dcode->m_Drill.y =
756  KiROUND( ReadDouble( aText ) * conv_scale );
757 
759  }
760  dcode->m_Defined = true;
761  break;
762 
763  case 'O': // oval
764  case 'R': // rect
765  dcode->m_Shape = (stdAperture == 'O') ? APT_OVAL : APT_RECT;
766 
767  while( *aText == ' ' )
768  aText++;
769 
770  if( *aText == 'X' ) // Second parameter: size Y
771  {
772  aText++;
773  dcode->m_Size.y =
774  KiROUND( ReadDouble( aText ) * conv_scale );
775  }
776 
777  while( *aText == ' ' )
778  aText++;
779 
780  if( *aText == 'X' ) // third parameter: drill size (or drill size X)
781  {
782  aText++;
783  dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
784  dcode->m_Drill.y = dcode->m_Drill.x;
786  }
787 
788  while( *aText == ' ' )
789  aText++;
790 
791  if( *aText == 'X' ) // fourth parameter: drill size Y
792  {
793  aText++;
794  dcode->m_Drill.y =
795  KiROUND( ReadDouble( aText ) * conv_scale );
797  }
798 
799  dcode->m_Defined = true;
800  break;
801 
802  case 'P':
803 
804  /* Regular polygon: a command line like %ADD12P,0.040X10X25X0.025X0.025X0.0150*%
805  * params are: <diameter>, X<edge count>, X<Rotation>, X<X hole dim>, X<Y hole dim>
806  */
807  dcode->m_Shape = APT_POLYGON;
808  while( *aText == ' ' )
809  aText++;
810 
811  if( *aText == 'X' )
812  {
813  aText++;
814  dcode->m_EdgesCount = ReadInt( aText );
815  }
816 
817  while( *aText == ' ' )
818  aText++;
819 
820  if( *aText == 'X' )
821  {
822  aText++;
823  dcode->m_Rotation = ReadDouble( aText );
824  }
825 
826  while( *aText == ' ' )
827  aText++;
828 
829  if( *aText == 'X' )
830  {
831  aText++;
832  dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
833  dcode->m_Drill.y = dcode->m_Drill.x;
835  }
836 
837  while( *aText == ' ' )
838  aText++;
839 
840  if( *aText == 'X' )
841  {
842  aText++;
843  dcode->m_Drill.y = KiROUND( ReadDouble( aText ) * conv_scale );
845  }
846  dcode->m_Defined = true;
847  break;
848  }
849  }
850  else // aText[0] starts an aperture macro name
851  {
852  APERTURE_MACRO am_lookup;
853 
854  while( *aText && *aText != '*' && *aText != ',' )
855  am_lookup.name.Append( *aText++ );
856 
857  // When an aperture definition is like %AMLINE2* 22,1,$1,$2,0,0,-45*
858  // the ADDxx<MACRO_NAME> command has parameters, like %ADD14LINE2,0.8X0.5*%
859  if( *aText == ',' )
860  { // Read aperture macro parameters and store them
861  aText++; // aText points the first parameter
862 
863  while( *aText && *aText != '*' )
864  {
865  double param = ReadDouble( aText );
866  dcode->AppendParam( param );
867 
868  while( isspace( *aText ) )
869  aText++;
870 
871  // Skip 'X' separator:
872  if( *aText == 'X' || *aText == 'x' )
873  aText++;
874  }
875  }
876 
877  // lookup the aperture macro here.
878  APERTURE_MACRO* pam = FindApertureMacro( am_lookup );
879 
880  if( !pam )
881  {
882  msg.Printf( wxT( "RS274X: aperture macro %s not found\n" ),
883  TO_UTF8( am_lookup.name ) );
884  AddMessageToList( msg );
885  ok = false;
886  break;
887  }
888 
889  dcode->m_Shape = APT_MACRO;
890  dcode->SetMacro( pam );
891  dcode->m_Defined = true;
892  }
893 
894  break;
895 
896  default:
897  ok = false;
898  break;
899  }
900 
901  ignore_unused( seq_len );
902 
903  ok = GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
904 
905  return ok;
906 }
907 
908 
909 bool GERBER_FILE_IMAGE::GetEndOfBlock( char* aBuff, unsigned int aBuffSize, char*& aText, FILE* gerber_file )
910 {
911  for( ; ; )
912  {
913  while( (aText < aBuff + aBuffSize) && *aText )
914  {
915  if( *aText == '*' )
916  return true;
917 
918  if( *aText == '%' )
919  return true;
920 
921  aText++;
922  }
923 
924  if( fgets( aBuff, aBuffSize, gerber_file ) == nullptr )
925  break;
926 
927  m_LineNum++;
928  aText = aBuff;
929  }
930 
931  return false;
932 }
933 
934 
935 char* GERBER_FILE_IMAGE::GetNextLine( char *aBuff, unsigned int aBuffSize, char* aText, FILE* aFile )
936 {
937  for( ; ; )
938  {
939  switch (*aText )
940  {
941  case ' ': // skip blanks
942  case '\n':
943  case '\r': // Skip line terminators
944  ++aText;
945  break;
946 
947  case 0: // End of text found in aBuff: Read a new string
948  if( fgets( aBuff, aBuffSize, aFile ) == nullptr )
949  return nullptr;
950 
951  m_LineNum++;
952  aText = aBuff;
953  return aText;
954 
955  default:
956  return aText;
957  }
958  }
959  return aText;
960 }
961 
962 
963 bool GERBER_FILE_IMAGE::ReadApertureMacro( char *aBuff, unsigned int aBuffSize,
964  char*& aText,
965  FILE* gerber_file )
966 {
967  wxString msg;
968  APERTURE_MACRO am;
969 
970  // read macro name
971  while( *aText )
972  {
973  if( *aText == '*' )
974  {
975  ++aText;
976  break;
977  }
978 
979  am.name.Append( *aText++ );
980  }
981 
982  // Read aperture macro parameters
983  for( ; ; )
984  {
985  if( *aText == '*' )
986  ++aText;
987 
988  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
989 
990  if( aText == nullptr ) // End of File
991  return false;
992 
993  // aText points the beginning of a new line.
994 
995  // Test for the last line in aperture macro lis:
996  // last line is % or *% sometime found.
997  if( *aText == '*' )
998  ++aText;
999 
1000  if( *aText == '%' )
1001  break; // exit with aText still pointing at %
1002 
1003  int paramCount = 0; // will be set to the minimal parameters count,
1004  // depending on the actual primitive
1005  int primitive_type = AMP_UNKNOWN;
1006  // Test for a valid symbol at the beginning of a description:
1007  // it can be: a parameter declaration like $1=$2/4
1008  // or a digit (macro primitive selection)
1009  // all other symbols are illegal.
1010  if( *aText == '$' ) // local parameter declaration, inside the aperture macro
1011  {
1012  am.m_localparamStack.push_back( AM_PARAM() );
1013  AM_PARAM& param = am.m_localparamStack.back();
1014  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1015  if( aText == nullptr) // End of File
1016  return false;
1017  param.ReadParam( aText );
1018  continue;
1019  }
1020  else if( !isdigit(*aText) ) // Ill. symbol
1021  {
1022  msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": ill. symbol, line: \"%s\"" ),
1023  am.name, FROM_UTF8( aBuff ) );
1024  AddMessageToList( msg );
1025  primitive_type = AMP_COMMENT;
1026  }
1027  else
1028  primitive_type = ReadInt( aText );
1029 
1030  bool is_comment = false;
1031 
1032  switch( primitive_type )
1033  {
1034  case AMP_COMMENT: // lines starting by 0 are a comment
1035  paramCount = 0;
1036  is_comment = true;
1037 
1038  // Skip comment
1039  while( *aText && ( *aText != '*' ) )
1040  aText++;
1041 
1042  break;
1043 
1044  case AMP_CIRCLE:
1045  paramCount = 4; // minimal count. can have a optional parameter (rotation)
1046  break;
1047 
1048  case AMP_LINE2:
1049  case AMP_LINE20:
1050  paramCount = 7;
1051  break;
1052 
1053  case AMP_LINE_CENTER:
1054  case AMP_LINE_LOWER_LEFT:
1055  paramCount = 6;
1056  break;
1057 
1058  case AMP_EOF:
1059  paramCount = 0;
1060  break;
1061 
1062  case AMP_OUTLINE:
1063  paramCount = 4; // partial count. other parameters are vertices and rotation
1064  // Second parameter is vertice (coordinate pairs) count.
1065  break;
1066 
1067  case AMP_POLYGON:
1068  paramCount = 6;
1069  break;
1070 
1071  case AMP_MOIRE:
1072  paramCount = 9;
1073  break;
1074 
1075  case AMP_THERMAL:
1076  paramCount = 6;
1077  break;
1078 
1079  default:
1080  msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Invalid primitive id code %d, line %d: \"%s\"" ),
1081  am.name, primitive_type, m_LineNum, FROM_UTF8( aBuff ) );
1082  AddMessageToList( msg );
1083  return false;
1084  }
1085 
1086  if( is_comment )
1087  continue;
1088 
1089  AM_PRIMITIVE prim( m_GerbMetric );
1090  prim.primitive_id = (AM_PRIMITIVE_ID) primitive_type;
1091  int ii;
1092 
1093  for( ii = 0; ii < paramCount && *aText && *aText != '*'; ++ii )
1094  {
1095  prim.params.push_back( AM_PARAM() );
1096 
1097  AM_PARAM& param = prim.params.back();
1098 
1099  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1100 
1101  if( aText == nullptr) // End of File
1102  return false;
1103 
1104  param.ReadParam( aText );
1105  }
1106 
1107  if( ii < paramCount )
1108  {
1109  // maybe some day we can throw an exception and track a line number
1110  msg.Printf( "RS274X: read macro descr type %d: read %d parameters, insufficient parameters\n",
1111  prim.primitive_id, ii );
1112  AddMessageToList( msg );
1113  }
1114 
1115  // there are more parameters to read if this is an AMP_OUTLINE
1116  if( prim.primitive_id == AMP_OUTLINE )
1117  {
1118  // so far we have read [0]:exposure, [1]:#points, [2]:X start, [3]: Y start
1119  // Now read all the points, plus trailing rotation in degrees.
1120 
1121  // params[1] is a count of polygon points, so it must be given
1122  // in advance, i.e. be immediate.
1123  wxASSERT( prim.params[1].IsImmediate() );
1124 
1125  paramCount = (int) prim.params[1].GetValue( nullptr ) * 2 + 1;
1126 
1127  for( int jj = 0; jj < paramCount && *aText != '*'; ++jj )
1128  {
1129  prim.params.push_back( AM_PARAM() );
1130 
1131  AM_PARAM& param = prim.params.back();
1132 
1133  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1134 
1135  if( aText == nullptr ) // End of File
1136  return false;
1137 
1138  param.ReadParam( aText );
1139  }
1140  }
1141 
1142  // AMP_CIRCLE can have a optional parameter (rotation)
1143  if( prim.primitive_id == AMP_CIRCLE && aText && *aText != '*' )
1144  {
1145  prim.params.push_back( AM_PARAM() );
1146  AM_PARAM& param = prim.params.back();
1147  param.ReadParam( aText );
1148  }
1149 
1150  am.primitives.push_back( prim );
1151  }
1152 
1153  m_aperture_macros.insert( am );
1154 
1155  return true;
1156 }
1157 
APERTURE_MACRO_SET m_aperture_macros
Handle special data (items attributes) during plot.
X2_ATTRIBUTE_FILEFUNCTION ( from TF.FileFunction in Gerber file) Example file function: TF....
int m_LineNum
< Line number of the gerber file while reading.
void AddMessageToList(const wxString &aMessage)
Add a message to the message list.
X2_ATTRIBUTE_FILEFUNCTION * m_FileFunction
The attribute value consists of a number of substrings separated by a comma.
wxString name
The name of the aperture macro.
Definition: am_primitive.h:231
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
Implementation of conversion functions that require both schematic and board internal units.
print info associated to a component (TO.C attribute)
wxSize m_Size
Horizontal and vertical dimensions.
Definition: dcode.h:188
int ReadInt(char *&text, bool aSkipSeparator=true)
Read an integer from an ASCII character buffer.
Definition: dcode.h:52
bool m_ImageJustifyXCenter
< Image Justify Center on X axis (default = false).
APERTURE_T m_Shape
shape ( Line, rectangle, circle , oval .. )
Definition: dcode.h:189
double m_Rotation
shape rotation in degrees
Definition: dcode.h:194
wxSize m_Drill
dimension of the hole (if any) (drill file)
Definition: dcode.h:191
wxRealPoint m_StepForRepeat
bool m_ImageJustifyYCenter
Image Justify Offset on XY axis (default = 0,0).
wxSize m_FmtLen
Image rotation (0, 90, 180, 270 only) in degrees.
wxString m_Cmpref
the component reference parent of the data
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
wxSize m_FmtScale
< Fmt 2.3: m_FmtScale = 3, fmt 3.4: m_FmtScale = 4.
int ReadXCommandID(char *&text)
Read two bytes of data and assembles them into an int with the first byte in the sequence put into th...
Definition: rs274x.cpp:115
Definition: dcode.h:51
bool ReadParam(char *&aText)
Read one aperture macro parameter.
Definition: am_param.cpp:177
AM_PARAMS m_localparamStack
Definition: am_primitive.h:240
This file contains miscellaneous commonly used macros and functions.
GBR_DATA_FIELD m_PadPinFunction
for a pad: the pin function (defined in schematic)
bool m_IsX2_file
< True if a X2 gerber attribute was found in file.
bool ExecuteRS274XCommand(int aCommand, char *aBuff, unsigned int aBuffSize, char *&aText)
Execute a RS274X command.
Definition: rs274x.cpp:198
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
char * GetNextLine(char *aBuff, unsigned int aBuffSize, char *aText, FILE *aFile)
Test for an end of line.
Definition: rs274x.cpp:935
void SetField(const wxString &aField, bool aUseUTF8, bool aEscapeString)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
double ReadDouble(char *&text, bool aSkipSeparator=true)
Read a double precision floating point number from an ASCII character buffer.
bool m_Defined
false if the aperture is not defined in the header
Definition: dcode.h:199
void AppendParam(double aValue)
Add a parameter to the D_CODE parameter list.
Definition: dcode.h:92
Hold a parameter value for an "aperture macro" as defined within standard RS274X.
Definition: am_param.h:284
GBR_DATA_FIELD m_Padname
for a flashed pad: the pad name ((TO.P attribute)
wxString m_Netname
for items associated to a net: the netname
#define _(s)
APERTURE_MACRO * FindApertureMacro(const APERTURE_MACRO &aLookup)
Look up a previously read in aperture macro.
bool m_Relative
< false = absolute Coord, true = relative Coord.
bool m_StepForRepeatMetric
bool ReadApertureMacro(char *aBuff, unsigned int aBuffSize, char *&text, FILE *gerber_file)
Read in an aperture macro and saves it in m_aperture_macros.
Definition: rs274x.cpp:963
wxString m_AperFunction
the aperture attribute (created by a TA.AperFunction command).
Definition: dcode.h:200
#define CODE(x, y)
Definition: rs274x.cpp:43
void RemoveAttribute(X2_ATTRIBUTE &aAttribute)
Called when a TD command is found the Gerber file.
APERTURE_DEF_HOLETYPE m_DrillShape
shape of the hole (0 = no hole, round = 1, rect = 2).
Definition: dcode.h:192
bool m_Has_DCode
< True if has DCodes in file or false if no DCodes found. Perhaps deprecated RS274D file.
RS274X_PARAMETERS
Definition: rs274x.cpp:56
AM_PRIMITIVES primitives
A sequence of AM_PRIMITIVEs.
Definition: am_primitive.h:232
A gerber DCODE (also called Aperture) definition.
Definition: dcode.h:80
Definition: rs274x.cpp:63
GBR_NETLIST_METADATA m_NetAttributeDict
bool ReadRS274XCommand(char *aBuff, unsigned int aBuffSize, char *&aText)
Read a single RS274X command terminated with a %.
Definition: rs274x.cpp:143
An aperture macro primitive as given in Table 3 of http://gerbv.sourceforge.net/docs/rs274xrevd_e....
Definition: am_primitive.h:93
void SetMacro(APERTURE_MACRO *aMacro)
Definition: dcode.h:120
bool GetEndOfBlock(char *aBuff, unsigned int aBuffSize, char *&aText, FILE *aGerberFile)
Definition: rs274x.cpp:909
#define IU_PER_MILS
Definition: plotter.cpp:136
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
void ignore_unused(const T &)
Definition: ignore.h:24
int m_EdgesCount
in aperture definition Polygon only: number of edges for the polygon
Definition: dcode.h:195
print info associated to a flashed pad (TO.P attribute)
wxString FormatStringFromGerber(const wxString &aString)
Convert a gerber string into a 16 bit Unicode string.
AM_PRIMITIVE_ID
The set of all "aperture macro primitives" (primitive numbers).
Definition: am_primitive.h:70
Support the "aperture macro" defined within standard RS274X.
Definition: am_primitive.h:167
print info associated to a net (TO.N attribute)
D_CODE * GetDCODEOrCreate(int aDCODE, bool aCreateIfNoExist=true)
Return a pointer to the D_CODE within this GERBER for the given aDCODE.
AM_PARAMS params
A sequence of parameters used by.
Definition: am_primitive.h:97
GERBER_LAYER & GetLayerParams()
int m_ImageRotation
Local rotation in degrees added to m_ImageRotation.
AM_PRIMITIVE_ID primitive_id
The primitive type.
Definition: am_primitive.h:96
int m_NetAttribType
the type of net info (used to define the gerber string to create)