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 
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 
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 
150 static const wxString fromGerberString( const wxString& aGbrString )
151 {
152  wxString text;
153 
154  for( unsigned ii = 0; ii < aGbrString.size(); ++ii )
155  {
156  if( aGbrString[ii] == '\\' )
157  {
158  unsigned value = 0;
159 
160  for( int jj = 0; jj < 4; jj++ )
161  { // Convert 4 hexa digits to binary value:
162  ii++;
163  value <<= 4;
164  int digit = aGbrString[ii];
165 
166  if( digit >= '0' && digit <= '9' )
167  digit -= '0';
168  else if( digit >= 'A' && digit <= 'F' )
169  digit -= 'A' - 10;
170  else if( digit >= 'a' && digit <= 'f' )
171  digit -= 'a' - 10;
172  else digit = 0;
173 
174  value += digit & 0xF;
175  }
176 
177  text.Append( wxUniChar( value ) );
178  }
179  else
180  text.Append( aGbrString[ii] );
181  }
182 
183  return text;
184 }
185 
186 bool GERBER_FILE_IMAGE::ReadRS274XCommand( char *aBuff, unsigned int aBuffSize, char*& aText )
187 {
188  bool ok = true;
189  int code_command;
190 
191  aText++;
192 
193  for( ; ; )
194  {
195  while( *aText )
196  {
197  switch( *aText )
198  {
199  case '%': // end of command
200  aText++;
202  goto exit; // success completion
203 
204  case ' ':
205  case '\r':
206  case '\n':
207  aText++;
208  break;
209 
210  case '*':
211  aText++;
212  break;
213 
214  default:
215  code_command = ReadXCommandID( aText );
216  ok = ExecuteRS274XCommand( code_command, aBuff, aBuffSize, aText );
217  if( !ok )
218  goto exit;
219  break;
220  }
221  }
222 
223  // end of current line, read another one.
224  if( fgets( aBuff, aBuffSize, m_Current_File ) == NULL )
225  {
226  // end of file
227  ok = false;
228  break;
229  }
230  m_LineNum++;
231  aText = aBuff;
232  }
233 
234 exit:
235  return ok;
236 }
237 
238 
239 bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff,
240  unsigned int aBuffSize, char*& aText )
241 {
242  int code;
243  int seq_len; // not used, just provided
244  int seq_char;
245  bool ok = true;
246  wxString msg;
247  double fcoord;
248  bool x_fmt_known = false;
249  bool y_fmt_known = false;
250 
251  // conv_scale = scaling factor from inch to Internal Unit
252  double conv_scale = IU_PER_MILS * 1000;
253 
254  if( m_GerbMetric )
255  conv_scale /= 25.4;
256 
257  switch( aCommand )
258  {
259  case FORMAT_STATEMENT:
260  seq_len = 2;
261 
262  while( *aText != '*' )
263  {
264  switch( *aText )
265  {
266  case ' ':
267  aText++;
268  break;
269 
270  case 'D': // Non-standard option for all zeros (leading + tailing)
271  msg.Printf( _( "RS274X: Invalid GERBER format command '%c' at line %d: \"%s\"" ),
272  'D', m_LineNum, aBuff );
273  AddMessageToList( msg );
274  msg.Printf( _("GERBER file \"%s\" may not display as intended." ),
275  m_FileName.ToAscii() );
276  AddMessageToList( msg );
277  // Fallthrough
278 
279  case 'L': // No Leading 0
280  m_NoTrailingZeros = false;
281  aText++;
282  break;
283 
284  case 'T': // No trailing 0
285  m_NoTrailingZeros = true;
286  aText++;
287  break;
288 
289  case 'A': // Absolute coord
290  m_Relative = false;
291  aText++;
292  break;
293 
294  case 'I': // Relative coord
295  m_Relative = true;
296  aText++;
297  break;
298 
299  case 'G':
300  case 'N': // Sequence code (followed by one digit: the sequence len)
301  // (sometimes found before the X,Y sequence)
302  // Obscure option
303  aText++;
304  seq_char = *aText++;
305  if( (seq_char >= '0') && (seq_char <= '9') )
306  seq_len = seq_char - '0';
307  break;
308 
309  case 'M': // Sequence code (followed by one digit: the sequence len)
310  // (sometimes found after the X,Y sequence)
311  // Obscure option
312  code = *aText++;
313  if( ( *aText >= '0' ) && ( *aText<= '9' ) )
314  aText++; // skip the digit
315  break;
316 
317  case 'X':
318  case 'Y':
319  {
320  code = *(aText++);
321  char ctmp = *(aText++) - '0';
322  if( code == 'X' )
323  {
324  x_fmt_known = true;
325  // number of digits after the decimal point (0 to 7 allowed)
326  m_FmtScale.x = *aText - '0';
327  m_FmtLen.x = ctmp + m_FmtScale.x;
328 
329  // m_FmtScale is 0 to 7
330  // (Old Gerber specification was 0 to 6)
331  if( m_FmtScale.x < 0 )
332  m_FmtScale.x = 0;
333  if( m_FmtScale.x > 7 )
334  m_FmtScale.x = 7;
335  }
336  else
337  {
338  y_fmt_known = true;
339  m_FmtScale.y = *aText - '0';
340  m_FmtLen.y = ctmp + m_FmtScale.y;
341  if( m_FmtScale.y < 0 )
342  m_FmtScale.y = 0;
343  if( m_FmtScale.y > 7 )
344  m_FmtScale.y = 7;
345  }
346  aText++;
347  }
348  break;
349 
350  case '*':
351  break;
352 
353  default:
354  msg.Printf( wxT( "Unknown id (%c) in FS command" ),
355  *aText );
356  AddMessageToList( msg );
357  GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
358  ok = false;
359  break;
360  }
361  }
362  if( !x_fmt_known || !y_fmt_known )
363  AddMessageToList( wxT( "RS274X: Format Statement (FS) without X or Y format" ) );
364 
365  break;
366 
367  case AXIS_SELECT: // command ASAXBY*% or %ASAYBX*%
368  m_SwapAxis = false;
369  if( strncasecmp( aText, "AYBX", 4 ) == 0 )
370  m_SwapAxis = true;
371  break;
372 
373  case MIRROR_IMAGE: // command %MIA0B0*%, %MIA0B1*%, %MIA1B0*%, %MIA1B1*%
374  m_MirrorA = m_MirrorB = 0;
375  while( *aText && *aText != '*' )
376  {
377  switch( *aText )
378  {
379  case 'A': // Mirror A axis ?
380  aText++;
381  if( *aText == '1' )
382  m_MirrorA = true;
383  break;
384 
385  case 'B': // Mirror B axis ?
386  aText++;
387  if( *aText == '1' )
388  m_MirrorB = true;
389  break;
390 
391  default:
392  aText++;
393  break;
394  }
395  }
396  break;
397 
398  case MODE_OF_UNITS:
399  code = ReadXCommandID( aText );
400  if( code == INCH )
401  m_GerbMetric = false;
402  else if( code == MILLIMETER )
403  m_GerbMetric = true;
404  conv_scale = m_GerbMetric ? IU_PER_MILS / 25.4 : IU_PER_MILS;
405  break;
406 
407  case FILE_ATTRIBUTE: // Command %TF ...
408  m_IsX2_file = true;
409  {
411  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
412 
413  if( dummy.IsFileFunction() )
414  {
415  delete m_FileFunction;
417  }
418  else if( dummy.IsFileMD5() )
419  {
420  m_MD5_value = dummy.GetPrm( 1 );
421  }
422  else if( dummy.IsFilePart() )
423  {
424  m_PartString = dummy.GetPrm( 1 );
425  }
426  }
427  break;
428 
429  case APERTURE_ATTRIBUTE: // Command %TA
430  {
432  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
433 
434  if( dummy.GetAttribute() == ".AperFunction" )
435  {
436  m_AperFunction = dummy.GetPrm( 1 );
437 
438  // A few function values can have other parameters. Add them
439  for( int ii = 2; ii < dummy.GetPrmCount(); ii++ )
440  m_AperFunction << "," << dummy.GetPrm( ii );
441  }
442  }
443  break;
444 
445  case NET_ATTRIBUTE: // Command %TO currently %TO.P %TO.N and %TO.C
446  {
448 
449  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
450 
451  if( dummy.GetAttribute() == ".N" )
452  {
455  }
456  else if( dummy.GetAttribute() == ".C" )
457  {
460  }
461  else if( dummy.GetAttribute() == ".P" )
462  {
466  }
467  }
468  break;
469 
470  case REMOVE_APERTURE_ATTRIBUTE: // Command %TD ...
471  {
473  dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
474  RemoveAttribute( dummy );
475  }
476  break;
477 
478  case OFFSET: // command: OFAnnBnn (nn = float number) = layer Offset
479  m_Offset.x = m_Offset.y = 0;
480  while( *aText != '*' )
481  {
482  switch( *aText )
483  {
484  case 'A': // A axis offset in current unit (inch or mm)
485  aText++;
486  fcoord = ReadDouble( aText );
487  m_Offset.x = KiROUND( fcoord * conv_scale );
488  break;
489 
490  case 'B': // B axis offset in current unit (inch or mm)
491  aText++;
492  fcoord = ReadDouble( aText );
493  m_Offset.y = KiROUND( fcoord * conv_scale );
494  break;
495  }
496  }
497  break;
498 
499  case SCALE_FACTOR:
500  m_Scale.x = m_Scale.y = 1.0;
501  while( *aText != '*' )
502  {
503  switch( *aText )
504  {
505  case 'A': // A axis scale
506  aText++;
507  m_Scale.x = ReadDouble( aText );
508  break;
509 
510  case 'B': // B axis scale
511  aText++;
512  m_Scale.y = ReadDouble( aText );
513  break;
514  }
515  }
516  break;
517 
518  case IMAGE_OFFSET: // command: IOAnnBnn (nn = float number) = Image Offset
520  while( *aText != '*' )
521  {
522  switch( *aText )
523  {
524  case 'A': // A axis offset in current unit (inch or mm)
525  aText++;
526  fcoord = ReadDouble( aText );
527  m_ImageOffset.x = KiROUND( fcoord * conv_scale );
528  break;
529 
530  case 'B': // B axis offset in current unit (inch or mm)
531  aText++;
532  fcoord = ReadDouble( aText );
533  m_ImageOffset.y = KiROUND( fcoord * conv_scale );
534  break;
535  }
536  }
537  break;
538 
539  case IMAGE_ROTATION: // command IR0* or IR90* or IR180* or IR270*
540  if( strncasecmp( aText, "0*", 2 ) == 0 )
541  m_ImageRotation = 0;
542  else if( strncasecmp( aText, "90*", 3 ) == 0 )
543  m_ImageRotation = 90;
544  else if( strncasecmp( aText, "180*", 4 ) == 0 )
545  m_ImageRotation = 180;
546  else if( strncasecmp( aText, "270*", 4 ) == 0 )
547  m_ImageRotation = 270;
548  else
549  AddMessageToList( _( "RS274X: Command \"IR\" rotation value not allowed" ) );
550  break;
551 
552  case STEP_AND_REPEAT: // command SR, like %SRX3Y2I5.0J2*%
553  m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
555  GetLayerParams().m_StepForRepeat.x = 0.0; // offset for Step and Repeat command
557  GetLayerParams().m_YRepeatCount = 1; // The repeat count
558  GetLayerParams().m_StepForRepeatMetric = m_GerbMetric; // the step units
559  while( *aText && *aText != '*' )
560  {
561  switch( *aText )
562  {
563  case 'I': // X axis offset
564  aText++;
566  break;
567 
568  case 'J': // Y axis offset
569  aText++;
571  break;
572 
573  case 'X': // X axis repeat count
574  aText++;
576  break;
577 
578  case 'Y': // Y axis offset
579  aText++;
581  break;
582  default:
583  aText++;
584  break;
585  }
586  }
587  break;
588 
589  case IMAGE_JUSTIFY: // Command IJAnBn*
590  m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false)
591  m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false)
592  m_ImageJustifyOffset = wxPoint(0,0); // Image Justify Offset on XY axis (default = 0,0)
593  while( *aText && *aText != '*' )
594  {
595  // IJ command is (for A or B axis) AC or AL or A<coordinate>
596  switch( *aText )
597  {
598  case 'A': // A axis justify
599  aText++;
600  if( *aText == 'C' )
601  {
602  m_ImageJustifyXCenter = true;
603  aText++;
604  }
605  else if( *aText == 'L' )
606  {
607  m_ImageJustifyXCenter = true;
608  aText++;
609  }
610  else m_ImageJustifyOffset.x = KiROUND( ReadDouble( aText ) * conv_scale);
611  break;
612 
613  case 'B': // B axis justify
614  aText++;
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 m_ImageJustifyOffset.y = KiROUND( ReadDouble( aText ) * conv_scale);
626  break;
627  default:
628  aText++;
629  break;
630  }
631  }
636  break;
637 
638  case KNOCKOUT:
639  m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
640  msg = _( "RS274X: Command KNOCKOUT ignored by GerbView" ) ;
641  AddMessageToList( msg );
642  break;
643 
644  case ROTATE: // Layer rotation: command like %RO45*%
645  m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
646  m_LocalRotation =ReadDouble( aText ); // Store layer rotation in degrees
647  break;
648 
649  case IMAGE_NAME:
650  m_ImageName.Empty();
651  while( *aText != '*' )
652  {
653  m_ImageName.Append( *aText++ );
654  }
655 
656  break;
657 
658  case LOAD_NAME:
659  // %LN is a (deprecated) equivalentto G04: a comment
660  while( *aText && *aText != '*' )
661  {
662  aText++; // Skip text
663  }
664 
665  break;
666 
667  case IMAGE_POLARITY:
668  if( strncasecmp( aText, "NEG", 3 ) == 0 )
669  m_ImageNegative = true;
670  else
671  m_ImageNegative = false;
672 
673  break;
674 
675  case LOAD_POLARITY:
676  if( *aText == 'C' )
678  else
680 
681  break;
682 
683  case AP_MACRO: // lines like %AMMYMACRO*
684  // 5,1,8,0,0,1.08239X$1,22.5*
685  // %
686  /*ok = */ReadApertureMacro( aBuff, aBuffSize, aText, m_Current_File );
687  break;
688 
689  case AP_DEFINITION:
690  /* input example: %ADD30R,0.081800X0.101500*%
691  * Aperture definition has 4 options: C, R, O, P
692  * (Circle, Rect, Oval, regular Polygon)
693  * and shapes can have a hole (round or rectangular).
694  * All optional parameters values start by X
695  * at this point, text points to 2nd 'D'
696  */
697  if( *aText++ != 'D' )
698  {
699  ok = false;
700  break;
701  }
702 
703  m_Has_DCode = true;
704 
705  code = ReadInt( aText );
706 
707  D_CODE* dcode;
708  dcode = GetDCODEOrCreate( code );
709 
710  if( dcode == NULL )
711  break;
712 
714 
715  // at this point, text points to character after the ADD<num>,
716  // i.e. R in example above. If aText[0] is one of the usual
717  // apertures: (C,R,O,P), there is a comma after it.
718  if( aText[1] == ',' )
719  {
720  char stdAperture = *aText;
721 
722  aText += 2; // skip "C," for example
723 
724  dcode->m_Size.x = KiROUND( ReadDouble( aText ) * conv_scale );
725  dcode->m_Size.y = dcode->m_Size.x;
726 
727  switch( stdAperture ) // Aperture desceiption has optional parameters. Read them
728  {
729  case 'C': // Circle
730  dcode->m_Shape = APT_CIRCLE;
731  while( *aText == ' ' )
732  aText++;
733 
734  if( *aText == 'X' )
735  {
736  aText++;
737  dcode->m_Drill.x = dcode->m_Drill.y =
738  KiROUND( ReadDouble( aText ) * conv_scale );
740  }
741 
742  while( *aText == ' ' )
743  aText++;
744 
745  if( *aText == 'X' )
746  {
747  aText++;
748  dcode->m_Drill.y =
749  KiROUND( ReadDouble( aText ) * conv_scale );
750 
752  }
753  dcode->m_Defined = true;
754  break;
755 
756  case 'O': // oval
757  case 'R': // rect
758  dcode->m_Shape = (stdAperture == 'O') ? APT_OVAL : APT_RECT;
759 
760  while( *aText == ' ' )
761  aText++;
762 
763  if( *aText == 'X' )
764  {
765  aText++;
766  dcode->m_Size.y =
767  KiROUND( ReadDouble( aText ) * conv_scale );
768  }
769 
770  while( *aText == ' ' )
771  aText++;
772 
773  if( *aText == 'X' )
774  {
775  aText++;
776  dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
777  dcode->m_Drill.y = dcode->m_Drill.x;
779  }
780 
781  while( *aText == ' ' )
782  aText++;
783 
784  if( *aText == 'X' )
785  {
786  aText++;
787  dcode->m_Drill.y =
788  KiROUND( ReadDouble( aText ) * conv_scale );
790  }
791  dcode->m_Defined = true;
792  break;
793 
794  case 'P':
795 
796  /* Regular polygon: a command line like %ADD12P,0.040X10X25X0.025X0.025X0.0150*%
797  * params are: <diameter>, X<edge count>, X<Rotation>, X<X hole dim>, X<Y hole dim>
798  */
799  dcode->m_Shape = APT_POLYGON;
800  while( *aText == ' ' )
801  aText++;
802 
803  if( *aText == 'X' )
804  {
805  aText++;
806  dcode->m_EdgesCount = ReadInt( aText );
807  }
808 
809  while( *aText == ' ' )
810  aText++;
811 
812  if( *aText == 'X' )
813  {
814  aText++;
815  dcode->m_Rotation = ReadDouble( aText );
816  }
817 
818  while( *aText == ' ' )
819  aText++;
820 
821  if( *aText == 'X' )
822  {
823  aText++;
824  dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
825  dcode->m_Drill.y = dcode->m_Drill.x;
827  }
828 
829  while( *aText == ' ' )
830  aText++;
831 
832  if( *aText == 'X' )
833  {
834  aText++;
835  dcode->m_Drill.y = KiROUND( ReadDouble( aText ) * conv_scale );
837  }
838  dcode->m_Defined = true;
839  break;
840  }
841  }
842  else // aText[0] starts an aperture macro name
843  {
844  APERTURE_MACRO am_lookup;
845 
846  while( *aText && *aText != '*' && *aText != ',' )
847  am_lookup.name.Append( *aText++ );
848 
849  // When an aperture definition is like %AMLINE2* 22,1,$1,$2,0,0,-45*
850  // the ADDxx<MACRO_NAME> command has parameters, like %ADD14LINE2,0.8X0.5*%
851  if( *aText == ',' )
852  { // Read aperture macro parameters and store them
853  aText++; // aText points the first parameter
854 
855  while( *aText && *aText != '*' )
856  {
857  double param = ReadDouble( aText );
858  dcode->AppendParam( param );
859 
860  while( isspace( *aText ) )
861  aText++;
862 
863  // Skip 'X' separator:
864  if( *aText == 'X' || *aText == 'x' )
865  aText++;
866  }
867  }
868 
869  // lookup the aperture macro here.
870  APERTURE_MACRO* pam = FindApertureMacro( am_lookup );
871 
872  if( !pam )
873  {
874  msg.Printf( wxT( "RS274X: aperture macro %s not found\n" ),
875  TO_UTF8( am_lookup.name ) );
876  AddMessageToList( msg );
877  ok = false;
878  break;
879  }
880 
881  dcode->m_Shape = APT_MACRO;
882  dcode->SetMacro( pam );
883  }
884 
885  break;
886 
887  default:
888  ok = false;
889  break;
890  }
891 
892  (void) seq_len; // quiet g++, or delete the unused variable.
893 
894  ok = GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
895 
896  return ok;
897 }
898 
899 
900 bool GERBER_FILE_IMAGE::GetEndOfBlock( char* aBuff, unsigned int aBuffSize, char*& aText, FILE* gerber_file )
901 {
902  for( ; ; )
903  {
904  while( (aText < aBuff + aBuffSize) && *aText )
905  {
906  if( *aText == '*' )
907  return true;
908 
909  if( *aText == '%' )
910  return true;
911 
912  aText++;
913  }
914 
915  if( fgets( aBuff, aBuffSize, gerber_file ) == NULL )
916  break;
917 
918  m_LineNum++;
919  aText = aBuff;
920  }
921 
922  return false;
923 }
924 
925 
926 char* GERBER_FILE_IMAGE::GetNextLine( char *aBuff, unsigned int aBuffSize, char* aText, FILE* aFile )
927 {
928  for( ; ; )
929  {
930  switch (*aText )
931  {
932  case ' ': // skip blanks
933  case '\n':
934  case '\r': // Skip line terminators
935  ++aText;
936  break;
937 
938  case 0: // End of text found in aBuff: Read a new string
939  if( fgets( aBuff, aBuffSize, aFile ) == NULL )
940  return NULL;
941 
942  m_LineNum++;
943  aText = aBuff;
944  return aText;
945 
946  default:
947  return aText;
948  }
949  }
950  return aText;
951 }
952 
953 
954 bool GERBER_FILE_IMAGE::ReadApertureMacro( char *aBuff, unsigned int aBuffSize,
955  char*& aText,
956  FILE* gerber_file )
957 {
958  wxString msg;
959  APERTURE_MACRO am;
960 
961  // read macro name
962  while( *aText )
963  {
964  if( *aText == '*' )
965  {
966  ++aText;
967  break;
968  }
969 
970  am.name.Append( *aText++ );
971  }
972 
973  // Read aperture macro parameters
974  for( ; ; )
975  {
976  if( *aText == '*' )
977  ++aText;
978 
979  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
980 
981  if( aText == NULL ) // End of File
982  return false;
983 
984  // aText points the beginning of a new line.
985 
986  // Test for the last line in aperture macro lis:
987  // last line is % or *% sometime found.
988  if( *aText == '*' )
989  ++aText;
990 
991  if( *aText == '%' )
992  break; // exit with aText still pointing at %
993 
994  int paramCount = 0; // will be set to the minimal parameters count,
995  // depending on the actual primitive
996  int primitive_type = AMP_UNKNOWN;
997  // Test for a valid symbol at the beginning of a description:
998  // it can be: a parameter declaration like $1=$2/4
999  // or a digit (macro primitive selection)
1000  // all other symbols are illegal.
1001  if( *aText == '$' ) // local parameter declaration, inside the aperture macro
1002  {
1003  am.m_localparamStack.push_back( AM_PARAM() );
1004  AM_PARAM& param = am.m_localparamStack.back();
1005  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1006  if( aText == NULL) // End of File
1007  return false;
1008  param.ReadParam( aText );
1009  continue;
1010  }
1011  else if( !isdigit(*aText) ) // Ill. symbol
1012  {
1013  msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": ill. symbol, line: \"%s\"" ),
1014  GetChars( am.name ), GetChars( FROM_UTF8( aBuff ) ) );
1015  AddMessageToList( msg );
1016  primitive_type = AMP_COMMENT;
1017  }
1018  else
1019  primitive_type = ReadInt( aText );
1020 
1021  bool is_comment = false;
1022 
1023  switch( primitive_type )
1024  {
1025  case AMP_COMMENT: // lines starting by 0 are a comment
1026  paramCount = 0;
1027  is_comment = true;
1028  // Skip comment
1029  while( *aText && ( *aText != '*' ) )
1030  aText++;
1031  break;
1032 
1033  case AMP_CIRCLE:
1034  paramCount = 4; // minimal count. can have a optional parameter (rotation)
1035  break;
1036 
1037  case AMP_LINE2:
1038  case AMP_LINE20:
1039  paramCount = 7;
1040  break;
1041 
1042  case AMP_LINE_CENTER:
1043  case AMP_LINE_LOWER_LEFT:
1044  paramCount = 6;
1045  break;
1046 
1047  case AMP_EOF:
1048  paramCount = 0;
1049  break;
1050 
1051  case AMP_OUTLINE:
1052  paramCount = 4;
1053  break;
1054 
1055  case AMP_POLYGON:
1056  paramCount = 6;
1057  break;
1058 
1059  case AMP_MOIRE:
1060  paramCount = 9;
1061  break;
1062 
1063  case AMP_THERMAL:
1064  paramCount = 6;
1065  break;
1066 
1067  default:
1068  msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Invalid primitive id code %d, line %d: \"%s\"" ),
1069  GetChars( am.name ), primitive_type, m_LineNum, GetChars( FROM_UTF8( aBuff ) ) );
1070  AddMessageToList( msg );
1071  return false;
1072  }
1073 
1074  if( is_comment )
1075  continue;
1076 
1077  AM_PRIMITIVE prim( m_GerbMetric );
1078  prim.primitive_id = (AM_PRIMITIVE_ID) primitive_type;
1079  int ii;
1080 
1081  for( ii = 0; ii < paramCount && *aText && *aText != '*'; ++ii )
1082  {
1083  prim.params.push_back( AM_PARAM() );
1084 
1085  AM_PARAM& param = prim.params.back();
1086 
1087  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1088 
1089  if( aText == NULL) // End of File
1090  return false;
1091 
1092  param.ReadParam( aText );
1093  }
1094 
1095  if( ii < paramCount )
1096  {
1097  // maybe some day we can throw an exception and track a line number
1098  msg.Printf( wxT( "RS274X: read macro descr type %d: read %d parameters, insufficient parameters\n" ),
1099  prim.primitive_id, ii );
1100  AddMessageToList( msg );
1101  }
1102  // there are more parameters to read if this is an AMP_OUTLINE
1103  if( prim.primitive_id == AMP_OUTLINE )
1104  {
1105  // so far we have read [0]:exposure, [1]:#points, [2]:X start, [3]: Y start
1106  // Now read all the points, plus trailing rotation in degrees.
1107 
1108  // params[1] is a count of polygon points, so it must be given
1109  // in advance, i.e. be immediate.
1110  wxASSERT( prim.params[1].IsImmediate() );
1111 
1112  paramCount = (int) prim.params[1].GetValue( 0 ) * 2 + 1;
1113 
1114  for( int jj = 0; jj < paramCount && *aText != '*'; ++jj )
1115  {
1116  prim.params.push_back( AM_PARAM() );
1117 
1118  AM_PARAM& param = prim.params.back();
1119 
1120  aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1121 
1122  if( aText == NULL ) // End of File
1123  return false;
1124 
1125  param.ReadParam( aText );
1126  }
1127  }
1128 
1129  am.primitives.push_back( prim );
1130  }
1131 
1132  m_aperture_macros.insert( am );
1133 
1134  return true;
1135 }
1136 
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)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:120
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)
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: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: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)
executes a RS274X command
Definition: rs274x.cpp:239
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:926
#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:150
bool m_StepForRepeatMetric
bool IsFileFunction()
return true if the attribute is .FileFunction
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:954
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)
reads a single RS274X command terminated with a %
Definition: rs274x.cpp:186
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:900
#define IU_PER_MILS
Definition: plotter.cpp:134
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)
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)