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