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