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