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-2016 Jean-Pierre Charras jp.charras at wanadoo.fr
5  * Copyright (C) 1992-2016 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 
34 #include <gerbview.h>
35 #include <gerber_file_image.h>
36 #include <X2_gerber_attributes.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 extention 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 extention 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 extention 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  // theses 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 
123 static int ReadXCommand( char*& text )
124 {
125  int result;
126  int currbyte;
127 
128  if( text && *text )
129  {
130  currbyte = *text++;
131  result = ( currbyte & 0xFF ) << 8;
132  }
133  else
134  return -1;
135 
136  if( text && *text )
137  {
138  currbyte = *text++;
139  result += currbyte & 0xFF;
140  }
141  else
142  return -1;
143 
144  return result;
145 }
146 
156 static const wxString fromGerberString( const wxString& aGbrString )
157 {
158  wxString text;
159 
160  for( unsigned ii = 0; ii < aGbrString.size(); ++ii )
161  {
162  if( aGbrString[ii] == '\\' )
163  {
164  unsigned value = 0;
165 
166  for( int jj = 0; jj < 4; jj++ )
167  { // Convert 4 hexa digits to binary value:
168  ii++;
169  value <<= 4;
170  int digit = aGbrString[ii];
171 
172  if( digit >= '0' && digit <= '9' )
173  digit -= '0';
174  else if( digit >= 'A' && digit <= 'F' )
175  digit -= 'A' - 10;
176  else if( digit >= 'a' && digit <= 'f' )
177  digit -= 'a' - 10;
178  else digit = 0;
179 
180  value += digit & 0xF;
181  }
182 
183  text.Append( wxUniChar( value ) );
184  }
185  else
186  text.Append( aGbrString[ii] );
187  }
188 
189  return text;
190 }
191 
192 bool GERBER_FILE_IMAGE::ReadRS274XCommand( char *aBuff, unsigned int aBuffSize, char*& aText )
193 {
194  bool ok = true;
195  int code_command;
196 
197  aText++;
198 
199  for( ; ; )
200  {
201  while( *aText )
202  {
203  switch( *aText )
204  {
205  case '%': // end of command
206  aText++;
208  goto exit; // success completion
209 
210  case ' ':
211  case '\r':
212  case '\n':
213  aText++;
214  break;
215 
216  case '*':
217  aText++;
218  break;
219 
220  default:
221  code_command = ReadXCommand( aText );
222  ok = ExecuteRS274XCommand( code_command, aBuff, aBuffSize, aText );
223  if( !ok )
224  goto exit;
225  break;
226  }
227  }
228 
229  // end of current line, read another one.
230  if( fgets( aBuff, aBuffSize, m_Current_File ) == NULL )
231  {
232  // end of file
233  ok = false;
234  break;
235  }
236  m_LineNum++;
237  aText = aBuff;
238  }
239 
240 exit:
241  return ok;
242 }
243 
244 
245 bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff,
246  unsigned int aBuffSize, char*& aText )
247 {
248  int code;
249  int seq_len; // not used, just provided
250  int seq_char;
251  bool ok = true;
252  wxString msg;
253  double fcoord;
254  bool x_fmt_known = false;
255  bool y_fmt_known = false;
256 
257  // conv_scale = scaling factor from inch to Internal Unit
258  double conv_scale = IU_PER_MILS * 1000;
259  if( m_GerbMetric )
260  conv_scale /= 25.4;
261 
262 // DBG( printf( "%22s: Command <%c%c>\n", __func__, (command >> 8) & 0xFF, command & 0xFF ); )
263 
264  switch( aCommand )
265  {
266  case FORMAT_STATEMENT:
267  seq_len = 2;
268 
269  while( *aText != '*' )
270  {
271  switch( *aText )
272  {
273  case ' ':
274  aText++;
275  break;
276 
277  case 'D': // Non-standard option for all zeros (leading + tailing)
278  msg.Printf( _( "RS274X: Invalid GERBER format command '%c' at line %d: \"%s\"" ),
279  'D', m_LineNum, aBuff );
280  AddMessageToList( msg );
281  msg.Printf( _("GERBER file \"%s\" may not display as intended." ),
282  m_FileName.ToAscii() );
283  AddMessageToList( msg );
284  // Fallthrough
285 
286  case 'L': // No Leading 0
287  m_NoTrailingZeros = false;
288  aText++;
289  break;
290 
291  case 'T': // No trailing 0
292  m_NoTrailingZeros = true;
293  aText++;
294  break;
295 
296  case 'A': // Absolute coord
297  m_Relative = false;
298  aText++;
299  break;
300 
301  case 'I': // Relative coord
302  m_Relative = true;
303  aText++;
304  break;
305 
306  case 'G':
307  case 'N': // Sequence code (followed by one digit: the sequence len)
308  // (sometimes found before the X,Y sequence)
309  // Obscure option
310  aText++;
311  seq_char = *aText++;
312  if( (seq_char >= '0') && (seq_char <= '9') )
313  seq_len = seq_char - '0';
314  break;
315 
316  case 'M': // Sequence code (followed by one digit: the sequence len)
317  // (sometimes found after the X,Y sequence)
318  // Obscure option
319  code = *aText++;
320  if( ( *aText >= '0' ) && ( *aText<= '9' ) )
321  aText++; // skip the digit
322  break;
323 
324  case 'X':
325  case 'Y':
326  {
327  code = *(aText++);
328  char ctmp = *(aText++) - '0';
329  if( code == 'X' )
330  {
331  x_fmt_known = true;
332  // number of digits after the decimal point (0 to 7 allowed)
333  m_FmtScale.x = *aText - '0';
334  m_FmtLen.x = ctmp + m_FmtScale.x;
335 
336  // m_FmtScale is 0 to 7
337  // (Old Gerber specification was 0 to 6)
338  if( m_FmtScale.x < 0 )
339  m_FmtScale.x = 0;
340  if( m_FmtScale.x > 7 )
341  m_FmtScale.x = 7;
342  }
343  else
344  {
345  y_fmt_known = true;
346  m_FmtScale.y = *aText - '0';
347  m_FmtLen.y = ctmp + m_FmtScale.y;
348  if( m_FmtScale.y < 0 )
349  m_FmtScale.y = 0;
350  if( m_FmtScale.y > 7 )
351  m_FmtScale.y = 7;
352  }
353  aText++;
354  }
355  break;
356 
357  case '*':
358  break;
359 
360  default:
361  msg.Printf( wxT( "Unknown id (%c) in FS command" ),
362  *aText );
363  AddMessageToList( msg );
364  GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
365  ok = false;
366  break;
367  }
368  }
369  if( !x_fmt_known || !y_fmt_known )
370  AddMessageToList( wxT( "RS274X: Format Statement (FS) without X or Y format" ) );
371 
372  break;
373 
374  case AXIS_SELECT: // command ASAXBY*% or %ASAYBX*%
375  m_SwapAxis = false;
376  if( strncasecmp( aText, "AYBX", 4 ) == 0 )
377  m_SwapAxis = true;
378  break;
379 
380  case MIRROR_IMAGE: // command %MIA0B0*%, %MIA0B1*%, %MIA1B0*%, %MIA1B1*%
381  m_MirrorA = m_MirrorB = 0;
382  while( *aText && *aText != '*' )
383  {
384  switch( *aText )
385  {
386  case 'A': // Mirror A axis ?
387  aText++;
388  if( *aText == '1' )
389  m_MirrorA = true;
390  break;
391 
392  case 'B': // Mirror B axis ?
393  aText++;
394  if( *aText == '1' )
395  m_MirrorB = true;
396  break;
397 
398  default:
399  aText++;
400  break;
401  }
402  }
403  break;
404 
405  case MODE_OF_UNITS:
406  code = ReadXCommand( aText );
407  if( code == INCH )
408  m_GerbMetric = false;
409  else if( code == MILLIMETER )
410  m_GerbMetric = true;
411  conv_scale = m_GerbMetric ? IU_PER_MILS / 25.4 : IU_PER_MILS;
412  break;
413 
414  case FILE_ATTRIBUTE: // Command %TF ...
415  m_IsX2_file = true;
416  {
418  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
419 
420  if( dummy.IsFileFunction() )
421  {
422  delete m_FileFunction;
424  }
425  else if( dummy.IsFileMD5() )
426  {
427  m_MD5_value = dummy.GetPrm( 1 );
428  }
429  else if( dummy.IsFilePart() )
430  {
431  m_PartString = dummy.GetPrm( 1 );
432  }
433  }
434  break;
435 
436  case APERTURE_ATTRIBUTE: // Command %TA ... Not yet supported
437  {
439  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
440 
441  if( dummy.GetAttribute() == ".AperFunction" )
442  {
443  m_AperFunction = dummy.GetPrm( 1 );
444 
445  // A few function values can have other parameters. Add them
446  for( int ii = 2; ii < dummy.GetPrmCount(); ii++ )
447  m_AperFunction << "," << dummy.GetPrm( ii );
448  }
449  }
450  break;
451 
452  case NET_ATTRIBUTE: // Command %TO currently %TO.P %TO.N and %TO.C
453  {
455 
456  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
457 
458  if( dummy.GetAttribute() == ".N" )
459  {
462  }
463  else if( dummy.GetAttribute() == ".C" )
464  {
467  }
468  else if( dummy.GetAttribute() == ".P" )
469  {
473  }
474  }
475  break;
476 
477  case REMOVE_APERTURE_ATTRIBUTE: // Command %TD ...
478  {
480  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
481  RemoveAttribute( dummy );
482  }
483  break;
484 
485  case OFFSET: // command: OFAnnBnn (nn = float number) = layer Offset
486  m_Offset.x = m_Offset.y = 0;
487  while( *aText != '*' )
488  {
489  switch( *aText )
490  {
491  case 'A': // A axis offset in current unit (inch or mm)
492  aText++;
493  fcoord = ReadDouble( aText );
494  m_Offset.x = KiROUND( fcoord * conv_scale );
495  break;
496 
497  case 'B': // B axis offset in current unit (inch or mm)
498  aText++;
499  fcoord = ReadDouble( aText );
500  m_Offset.y = KiROUND( fcoord * conv_scale );
501  break;
502  }
503  }
504  break;
505 
506  case SCALE_FACTOR:
507  m_Scale.x = m_Scale.y = 1.0;
508  while( *aText != '*' )
509  {
510  switch( *aText )
511  {
512  case 'A': // A axis scale
513  aText++;
514  m_Scale.x = ReadDouble( aText );
515  break;
516 
517  case 'B': // B axis scale
518  aText++;
519  m_Scale.y = ReadDouble( aText );
520  break;
521  }
522  }
523  break;
524 
525  case IMAGE_OFFSET: // command: IOAnnBnn (nn = float number) = Image Offset
527  while( *aText != '*' )
528  {
529  switch( *aText )
530  {
531  case 'A': // A axis offset in current unit (inch or mm)
532  aText++;
533  fcoord = ReadDouble( aText );
534  m_ImageOffset.x = KiROUND( fcoord * conv_scale );
535  break;
536 
537  case 'B': // B axis offset in current unit (inch or mm)
538  aText++;
539  fcoord = ReadDouble( aText );
540  m_ImageOffset.y = KiROUND( fcoord * conv_scale );
541  break;
542  }
543  }
544  break;
545 
546  case IMAGE_ROTATION: // command IR0* or IR90* or IR180* or IR270*
547  if( strncasecmp( aText, "0*", 2 ) == 0 )
548  m_ImageRotation = 0;
549  else if( strncasecmp( aText, "90*", 3 ) == 0 )
550  m_ImageRotation = 90;
551  else if( strncasecmp( aText, "180*", 4 ) == 0 )
552  m_ImageRotation = 180;
553  else if( strncasecmp( aText, "270*", 4 ) == 0 )
554  m_ImageRotation = 270;
555  else
556  AddMessageToList( _( "RS274X: Command \"IR\" rotation value not allowed" ) );
557  break;
558 
559  case STEP_AND_REPEAT: // command SR, like %SRX3Y2I5.0J2*%
560  m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
562  GetLayerParams().m_StepForRepeat.x = 0.0; // offset for Step and Repeat command
564  GetLayerParams().m_YRepeatCount = 1; // The repeat count
565  GetLayerParams().m_StepForRepeatMetric = m_GerbMetric; // the step units
566  while( *aText && *aText != '*' )
567  {
568  switch( *aText )
569  {
570  case 'I': // X axis offset
571  aText++;
573  break;
574 
575  case 'J': // Y axis offset
576  aText++;
578  break;
579 
580  case 'X': // X axis repeat count
581  aText++;
583  break;
584 
585  case 'Y': // Y axis offset
586  aText++;
588  break;
589  default:
590  aText++;
591  break;
592  }
593  }
594  break;
595 
596  case IMAGE_JUSTIFY: // Command IJAnBn*
597  m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false)
598  m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false)
599  m_ImageJustifyOffset = wxPoint(0,0); // Image Justify Offset on XY axis (default = 0,0)
600  while( *aText && *aText != '*' )
601  {
602  // IJ command is (for A or B axis) AC or AL or A<coordinate>
603  switch( *aText )
604  {
605  case 'A': // A axis justify
606  aText++;
607  if( *aText == 'C' )
608  {
609  m_ImageJustifyXCenter = true;
610  aText++;
611  }
612  else if( *aText == 'L' )
613  {
614  m_ImageJustifyXCenter = true;
615  aText++;
616  }
617  else m_ImageJustifyOffset.x = KiROUND( ReadDouble( aText ) * conv_scale);
618  break;
619 
620  case 'B': // B axis justify
621  aText++;
622  if( *aText == 'C' )
623  {
624  m_ImageJustifyYCenter = true;
625  aText++;
626  }
627  else if( *aText == 'L' )
628  {
629  m_ImageJustifyYCenter = true;
630  aText++;
631  }
632  else m_ImageJustifyOffset.y = KiROUND( ReadDouble( aText ) * conv_scale);
633  break;
634  default:
635  aText++;
636  break;
637  }
638  }
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  while( *aText != '*' )
659  {
660  m_ImageName.Append( *aText++ );
661  }
662 
663  break;
664 
665  case LOAD_NAME:
666  // %LN is a (deprecated) equivalentto G04: a comment
667  while( *aText && *aText != '*' )
668  {
669  aText++; // Skip text
670  }
671 
672  break;
673 
674  case IMAGE_POLARITY:
675  if( strncasecmp( aText, "NEG", 3 ) == 0 )
676  m_ImageNegative = true;
677  else
678  m_ImageNegative = false;
679  DBG( printf( "%22s: IMAGE_POLARITY m_ImageNegative=%s\n", __func__,
680  m_ImageNegative ? "true" : "false" ); )
681  break;
682 
683  case LOAD_POLARITY:
684  if( *aText == 'C' )
686  else
688 
689  break;
690 
691  case AP_MACRO: // lines like %AMMYMACRO*
692  // 5,1,8,0,0,1.08239X$1,22.5*
693  // %
694  /*ok = */ReadApertureMacro( aBuff, aBuffSize, aText, m_Current_File );
695  break;
696 
697  case AP_DEFINITION:
698  /* input example: %ADD30R,0.081800X0.101500*%
699  * Aperture definition has 4 options: C, R, O, P
700  * (Circle, Rect, Oval, regular Polygon)
701  * and shapes can have a hole (round or rectangular).
702  * All optional parameters values start by X
703  * at this point, text points to 2nd 'D'
704  */
705  if( *aText++ != 'D' )
706  {
707  ok = false;
708  break;
709  }
710 
711  m_Has_DCode = true;
712 
713  code = ReadInt( aText );
714 
715  D_CODE* dcode;
716  dcode = GetDCODEOrCreate( code );
717 
718  if( dcode == NULL )
719  break;
720 
722 
723  // at this point, text points to character after the ADD<num>,
724  // i.e. R in example above. If aText[0] is one of the usual
725  // apertures: (C,R,O,P), there is a comma after it.
726  if( aText[1] == ',' )
727  {
728  char stdAperture = *aText;
729 
730  aText += 2; // skip "C," for example
731 
732  dcode->m_Size.x = KiROUND( ReadDouble( aText ) * conv_scale );
733  dcode->m_Size.y = dcode->m_Size.x;
734 
735  switch( stdAperture ) // Aperture desceiption has optional parameters. Read them
736  {
737  case 'C': // Circle
738  dcode->m_Shape = APT_CIRCLE;
739  while( *aText == ' ' )
740  aText++;
741 
742  if( *aText == 'X' )
743  {
744  aText++;
745  dcode->m_Drill.x = dcode->m_Drill.y =
746  KiROUND( ReadDouble( aText ) * conv_scale );
748  }
749 
750  while( *aText == ' ' )
751  aText++;
752 
753  if( *aText == 'X' )
754  {
755  aText++;
756  dcode->m_Drill.y =
757  KiROUND( ReadDouble( aText ) * conv_scale );
758 
760  }
761  dcode->m_Defined = true;
762  break;
763 
764  case 'O': // oval
765  case 'R': // rect
766  dcode->m_Shape = (stdAperture == 'O') ? APT_OVAL : APT_RECT;
767 
768  while( *aText == ' ' )
769  aText++;
770 
771  if( *aText == 'X' )
772  {
773  aText++;
774  dcode->m_Size.y =
775  KiROUND( ReadDouble( aText ) * conv_scale );
776  }
777 
778  while( *aText == ' ' )
779  aText++;
780 
781  if( *aText == 'X' )
782  {
783  aText++;
784  dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
785  dcode->m_Drill.y = dcode->m_Drill.x;
787  }
788 
789  while( *aText == ' ' )
790  aText++;
791 
792  if( *aText == 'X' )
793  {
794  aText++;
795  dcode->m_Drill.y =
796  KiROUND( ReadDouble( aText ) * conv_scale );
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  }
892 
893  break;
894 
895  default:
896  ok = false;
897  break;
898  }
899 
900  (void) seq_len; // quiet g++, or delete the unused variable.
901 
902  ok = GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
903 
904  return ok;
905 }
906 
907 
908 bool GERBER_FILE_IMAGE::GetEndOfBlock( char* aBuff, unsigned int aBuffSize, char*& aText, FILE* gerber_file )
909 {
910  for( ; ; )
911  {
912  while( (aText < aBuff + aBuffSize) && *aText )
913  {
914  if( *aText == '*' )
915  return true;
916 
917  if( *aText == '%' )
918  return true;
919 
920  aText++;
921  }
922 
923  if( fgets( aBuff, aBuffSize, gerber_file ) == NULL )
924  break;
925 
926  m_LineNum++;
927  aText = aBuff;
928  }
929 
930  return false;
931 }
932 
933 
934 char* GERBER_FILE_IMAGE::GetNextLine( char *aBuff, unsigned int aBuffSize, char* aText, FILE* aFile )
935 {
936  for( ; ; )
937  {
938  switch (*aText )
939  {
940  case ' ': // skip blanks
941  case '\n':
942  case '\r': // Skip line terminators
943  ++aText;
944  break;
945 
946  case 0: // End of text found in aBuff: Read a new string
947  if( fgets( aBuff, aBuffSize, aFile ) == NULL )
948  return NULL;
949 
950  m_LineNum++;
951  aText = aBuff;
952  return aText;
953 
954  default:
955  return aText;
956  }
957  }
958  return aText;
959 }
960 
961 
962 bool GERBER_FILE_IMAGE::ReadApertureMacro( char *aBuff, unsigned int aBuffSize,
963  char*& aText,
964  FILE* gerber_file )
965 {
966  wxString msg;
967  APERTURE_MACRO am;
968 
969  // read macro name
970  while( *aText )
971  {
972  if( *aText == '*' )
973  {
974  ++aText;
975  break;
976  }
977 
978  am.name.Append( *aText++ );
979  }
980 
981  // Read aperture macro parameters
982  for( ; ; )
983  {
984  if( *aText == '*' )
985  ++aText;
986 
987  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
988 
989  if( aText == NULL ) // End of File
990  return false;
991 
992  // aText points the beginning of a new line.
993 
994  // Test for the last line in aperture macro lis:
995  // last line is % or *% sometime found.
996  if( *aText == '*' )
997  ++aText;
998 
999  if( *aText == '%' )
1000  break; // exit with aText still pointing at %
1001 
1002  int paramCount = 0; // will be set to the minimal parameters count,
1003  // depending on the actual primitive
1004  int primitive_type = AMP_UNKNOWN;
1005  // Test for a valid symbol at the beginning of a description:
1006  // it can be: a parameter declaration like $1=$2/4
1007  // or a digit (macro primitive selection)
1008  // all other symbols are illegal.
1009  if( *aText == '$' ) // local parameter declaration, inside the aperture macro
1010  {
1011  am.m_localparamStack.push_back( AM_PARAM() );
1012  AM_PARAM& param = am.m_localparamStack.back();
1013  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1014  if( aText == NULL) // End of File
1015  return false;
1016  param.ReadParam( aText );
1017  continue;
1018  }
1019  else if( !isdigit(*aText) ) // Ill. symbol
1020  {
1021  msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": ill. symbol, line: \"%s\"" ),
1022  GetChars( am.name ), GetChars( FROM_UTF8( aBuff ) ) );
1023  AddMessageToList( msg );
1024  primitive_type = AMP_COMMENT;
1025  }
1026  else
1027  primitive_type = ReadInt( aText );
1028 
1029  bool is_comment = false;
1030 
1031  switch( primitive_type )
1032  {
1033  case AMP_COMMENT: // lines starting by 0 are a comment
1034  paramCount = 0;
1035  is_comment = true;
1036  // Skip comment
1037  while( *aText && ( *aText != '*' ) )
1038  aText++;
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;
1061  break;
1062 
1063  case AMP_POLYGON:
1064  paramCount = 6;
1065  break;
1066 
1067  case AMP_MOIRE:
1068  paramCount = 9;
1069  break;
1070 
1071  case AMP_THERMAL:
1072  paramCount = 6;
1073  break;
1074 
1075  default:
1076  msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Invalid primitive id code %d, line %d: \"%s\"" ),
1077  GetChars( am.name ), primitive_type, m_LineNum, GetChars( FROM_UTF8( aBuff ) ) );
1078  AddMessageToList( msg );
1079  return false;
1080  }
1081 
1082  if( is_comment )
1083  continue;
1084 
1085  AM_PRIMITIVE prim( m_GerbMetric );
1086  prim.primitive_id = (AM_PRIMITIVE_ID) primitive_type;
1087  int ii;
1088 
1089  for( ii = 0; ii < paramCount && *aText && *aText != '*'; ++ii )
1090  {
1091  prim.params.push_back( AM_PARAM() );
1092 
1093  AM_PARAM& param = prim.params.back();
1094 
1095  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1096 
1097  if( aText == NULL) // End of File
1098  return false;
1099 
1100  param.ReadParam( aText );
1101  }
1102 
1103  if( ii < paramCount )
1104  {
1105  // maybe some day we can throw an exception and track a line number
1106  msg.Printf( wxT( "RS274X: read macro descr type %d: read %d parameters, insufficient parameters\n" ),
1107  prim.primitive_id, ii );
1108  AddMessageToList( msg );
1109  }
1110  // there are more parameters to read if this is an AMP_OUTLINE
1111  if( prim.primitive_id == AMP_OUTLINE )
1112  {
1113  // so far we have read [0]:exposure, [1]:#points, [2]:X start, [3]: Y start
1114  // Now read all the points, plus trailing rotation in degrees.
1115 
1116  // params[1] is a count of polygon points, so it must be given
1117  // in advance, i.e. be immediate.
1118  wxASSERT( prim.params[1].IsImmediate() );
1119 
1120  paramCount = (int) prim.params[1].GetValue( 0 ) * 2 + 1;
1121 
1122  for( int jj = 0; jj < paramCount && *aText != '*'; ++jj )
1123  {
1124  prim.params.push_back( AM_PARAM() );
1125 
1126  AM_PARAM& param = prim.params.back();
1127 
1128  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1129 
1130  if( aText == NULL ) // End of File
1131  return false;
1132 
1133  param.ReadParam( aText );
1134  }
1135  }
1136 
1137  am.primitives.push_back( prim );
1138  }
1139 
1140  m_aperture_macros.insert( am );
1141 
1142  return true;
1143 }
1144 
APERTURE_MACRO_SET m_aperture_macros
a collection of APERTURE_MACROS, sorted by name
class 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
class 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:53
static int KiROUND(double v)
KiROUND rounds a floating point number to an int using "round halfway cases away from zero"...
Definition: common.h:107
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
const wxString & GetPrm(int aIdx)
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:171
AM_PARAMS m_localparamStack
Definition: am_primitive.h:174
This file contains miscellaneous commonly used macros and functions.
bool ExecuteRS274XCommand(int aCommand, char *aBuff, unsigned int aBuffSize, char *&aText)
Function ExecuteRS274XCommand executes 1 command.
Definition: rs274x.cpp:245
char * GetNextLine(char *aBuff, unsigned int aBuffSize, char *aText, FILE *aFile)
Function GetNextLine test for an end of line if an end of line is found: read a new line...
Definition: rs274x.cpp:934
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes...
Definition: macros.h:47
double ReadDouble(char *&text, bool aSkipSeparator=true)
Function ReadDouble reads a double from an ASCII character buffer.
bool IsFileMD5()
return true if the attribute is .MD5
bool m_Defined
false if the aperture is not defined in the header
Definition: dcode.h:104
wxString m_Padname
for a flashed pad: the pad name ((TO.P attribute)
void AppendParam(double aValue)
AppendParam() Add a parameter to the D_CODE parameter list.
Definition: dcode.h:122
bool IsFilePart()
return true if the attribute is .Part
Class AM_PARAM holds a parameter value for an "aperture macro" as defined within standard RS274X...
Definition: am_param.h:286
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.
static const wxString fromGerberString(const wxString &aGbrString)
Convert a string read from a gerber file to an unicode string Usual chars (ASCII7 values) are the onl...
Definition: rs274x.cpp:156
bool m_StepForRepeatMetric
bool IsFileFunction()
return true if the attribute is .FileFunction
bool ReadApertureMacro(char *aBuff, unsigned int aBuffSize, char *&text, FILE *gerber_file)
Function ReadApertureMacro reads in an aperture macro and saves it in m_aperture_macros.
Definition: rs274x.cpp:962
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)
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:55
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:92
AM_PRIMITIVES primitives
A sequence of AM_PRIMITIVEs.
Definition: am_primitive.h:166
Class D_CODE holds a gerber DCODE (also called Aperture) definition.
Definition: dcode.h:82
static LIB_PART * dummy()
Used when a LIB_PART is not found in library to draw a dummy shape This component is a 400 mils squar...
Definition: rs274x.cpp:62
GBR_NETLIST_METADATA m_NetAttributeDict
bool ReadRS274XCommand(char *aBuff, unsigned int aBuffSize, char *&aText)
Function ReadRS274XCommand reads a single RS274X command terminated with a %.
Definition: rs274x.cpp:192
Struct AM_PRIMITIVE holds an aperture macro primitive as given in Table 3 of http://gerbv.sourceforge.net/docs/rs274xrevd_e.pdf.
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:908
bool ParseAttribCmd(FILE *aFile, char *aBuffer, int aBuffSize, char *&aText, int &aLineNum)
parse a TF command terminated with a % and fill m_Prms by the parameters found.
int m_EdgesCount
in aperture definition Polygon only: number of edges for the polygon
Definition: dcode.h:100
The common library.
const wxString & GetAttribute()
print info associated to a flashed pad (TO.P attribute)
#define DBG(x)
Definition: fctsys.h:33
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
static int ReadXCommand(char *&text)
Function ReadXCommand reads in two bytes of data and assembles them into an int with the first byte i...
Definition: rs274x.cpp:123
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)