KiCad PCB EDA Suite
rs274x.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2007-2018 Jean-Pierre Charras jp.charras at wanadoo.fr
5 * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
29#include <base_units.h>
30#include <math/util.h> // for KiROUND
31
32#include <gerbview.h>
33#include <gerber_file_image.h>
34#include <ignore.h>
35#include <macros.h>
37#include <gbr_metadata.h>
38#include <wx/log.h>
39
40extern int ReadInt( char*& text, bool aSkipSeparator = true );
41extern double ReadDouble( char*& text, bool aSkipSeparator = true );
42
43
44#define CODE( x, y ) ( ( (x) << 8 ) + (y) )
45
46// See rs274xrevd_e.pdf, table 1: RS-274X parameters order of entry
47// in gerber files, when a coordinate is given (like X78Y600 or I0J80):
48// Y and Y are logical coordinates
49// A and B are plotter coordinates
50// Usually A = X, B = Y
51// But we can have A = Y, B = X and/or offset, mirror, scale;
52// Also:
53// Image is what you must plot (the entire data of the file).
54// Layer is just a set of data blocks with their parameters. An image can have more than one
55// layer so a gerber layer is not like a board layer or the graphic layers used in GerbView
56// to show a file.
58 // Directive parameters: single usage recommended
59 // Must be at the beginning of the file
60 AXIS_SELECT = CODE( 'A', 'S' ), // Default: A=X, B=Y
61 FORMAT_STATEMENT = CODE( 'F', 'S' ), // no default: this command must exists
62 MIRROR_IMAGE = CODE( 'M', 'I' ), // Default: mo mirror
63 MODE_OF_UNITS = CODE( 'M', 'O' ), // Default: inch
64 INCH = CODE( 'I', 'N' ),
65 MILLIMETER = CODE( 'M', 'M' ),
66 OFFSET = CODE( 'O', 'F' ), // Default: A = 0, B = 0
67 SCALE_FACTOR = CODE( 'S', 'F' ), // Default: A = 1.0, B = 1.0
68
69 // Image parameters:
70 // commands used only once at the beginning of the file, and are deprecated
71 IMAGE_JUSTIFY = CODE( 'I', 'J' ), // Default: no justification
72 IMAGE_NAME = CODE( 'I', 'N' ), // Default: void
73 IMAGE_OFFSET = CODE( 'I', 'O' ), // Default: A = 0, B = 0
74 IMAGE_POLARITY = CODE( 'I', 'P' ), // Default: Positive
75 IMAGE_ROTATION = CODE( 'I', 'R' ), // Default: 0
76
77 // Aperture parameters:
78 // Usually for the whole file
79 AP_DEFINITION = CODE( 'A', 'D' ),
80 AP_MACRO = CODE( 'A', 'M' ),
81
82 // X2 extension attribute commands
83 // Mainly are found standard attributes and user attributes
84 // standard attributes commands are:
85 // TF (file attribute) TO (net attribute)
86 // TA (aperture attribute) and TD (delete aperture attribute)
87 FILE_ATTRIBUTE = CODE( 'T', 'F' ),
88
89 // X2 extension Net attribute info
90 // Net attribute options are:
91 // TO (net attribute data): TO.CN or TO.P TO.N or TO.C
92 NET_ATTRIBUTE = CODE( 'T', 'O' ),
93
94 // X2 extension Aperture attribute TA
95 APERTURE_ATTRIBUTE = CODE( 'T', 'A' ),
96
97 // TD (delete aperture/object attribute):
98 // Delete aperture attribute added by %TA or Oblect attribute added b %TO
99 // TD (delete all) or %TD<attr name> to delete <attr name>.
100 // eg: TD.P or TD.N or TD.C ...
102
103 // Layer specific parameters
104 // May be used singly or may be layer specific
105 // These parameters are at the beginning of the file or layer
106 // and reset some layer parameters (like interpolation)
107 KNOCKOUT = CODE( 'K', 'O' ), // Default: off
108 STEP_AND_REPEAT = CODE( 'S', 'R' ), // Default: A = 1, B = 1
109 ROTATE = CODE( 'R', 'O' ), // Default: 0
110
111 LOAD_POLARITY = CODE( 'L', 'P' ), //LPC or LPD. Default: Dark (LPD)
112 LOAD_NAME = CODE( 'L', 'N' ), // Deprecated: equivalent to G04
113};
114
115
117{
118 /* reads two bytes of data and assembles them into an int with the first
119 * byte in the sequence put into the most significant part of a 16 bit value
120 */
121 int result;
122 int currbyte;
123
124 if( text && *text )
125 {
126 currbyte = *text++;
127 result = ( currbyte & 0xFF ) << 8;
128 }
129 else
130 return -1;
131
132 if( text && *text )
133 {
134 currbyte = *text++;
135 result += currbyte & 0xFF;
136 }
137 else
138 return -1;
139
140 return result;
141}
142
143
144bool GERBER_FILE_IMAGE::ReadRS274XCommand( char *aBuff, unsigned int aBuffSize, char*& aText )
145{
146 bool ok = true;
147 int code_command;
148
149 aText++;
150
151 for( ; ; )
152 {
153 while( *aText )
154 {
155 switch( *aText )
156 {
157 case '%': // end of command
158 aText++;
160 goto exit; // success completion
161
162 case ' ':
163 case '\r':
164 case '\n':
165 aText++;
166 break;
167
168 case '*':
169 aText++;
170 break;
171
172 default:
173 code_command = ReadXCommandID( aText );
174 ok = ExecuteRS274XCommand( code_command, aBuff, aBuffSize, aText );
175
176 if( !ok )
177 goto exit;
178
179 break;
180 }
181 }
182
183 // end of current line, read another one.
184 if( fgets( aBuff, aBuffSize, m_Current_File ) == nullptr )
185 {
186 // end of file
187 ok = false;
188 break;
189 }
190 m_LineNum++;
191 aText = aBuff;
192 }
193
194exit:
195 return ok;
196}
197
198
199bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff,
200 unsigned int aBuffSize, char*& aText )
201{
202 int code;
203 int seq_len; // not used, just provided
204 int seq_char;
205 bool ok = true;
206 wxString msg;
207 double fcoord;
208 bool x_fmt_known = false;
209 bool y_fmt_known = false;
210
211 // conv_scale = scaling factor from inch to Internal Unit
212 double conv_scale = gerbIUScale.IU_PER_MILS * 1000;
213
214 if( m_GerbMetric )
215 conv_scale /= 25.4;
216
217 switch( aCommand )
218 {
219 case FORMAT_STATEMENT:
220 seq_len = 2;
221
222 while( *aText != '*' )
223 {
224 switch( *aText )
225 {
226 case ' ':
227 aText++;
228 break;
229
230 case 'D': // Non-standard option for all zeros (leading + tailing)
231 msg.Printf( _( "RS274X: Invalid GERBER format command '%c' at line %d: \"%s\"" ),
232 'D', m_LineNum, aBuff );
233 AddMessageToList( msg );
234 msg.Printf( _("GERBER file \"%s\" may not display as intended." ),
235 m_FileName.ToAscii() );
236 AddMessageToList( msg );
238
239 case 'L': // No Leading 0
240 m_NoTrailingZeros = false;
241 aText++;
242 break;
243
244 case 'T': // No trailing 0
245 m_NoTrailingZeros = true;
246 aText++;
247 break;
248
249 case 'A': // Absolute coord
250 m_Relative = false;
251 aText++;
252 break;
253
254 case 'I': // Relative coord
255 m_Relative = true;
256 aText++;
257 break;
258
259 case 'G':
260 case 'N': // Sequence code (followed by one digit: the sequence len)
261 // (sometimes found before the X,Y sequence)
262 // Obscure option
263 aText++;
264 seq_char = *aText++;
265
266 if( (seq_char >= '0') && (seq_char <= '9') )
267 seq_len = seq_char - '0';
268
269 break;
270
271 case 'M': // Sequence code (followed by one digit: the sequence len)
272 // (sometimes found after the X,Y sequence)
273 // Obscure option
274 aText++;
275 code = *aText;
276
277 if( ( code >= '0' ) && ( code <= '9' ) )
278 aText++; // skip the digit
279
280 break;
281
282 case 'X':
283 case 'Y':
284 {
285 code = *(aText++);
286 char ctmp = *(aText++) - '0';
287
288 if( code == 'X' )
289 {
290 x_fmt_known = true;
291 // number of digits after the decimal point (0 to 7 allowed)
292 m_FmtScale.x = *aText - '0';
293 m_FmtLen.x = ctmp + m_FmtScale.x;
294
295 // m_FmtScale is 0 to 7
296 // (Old Gerber specification was 0 to 6)
297 if( m_FmtScale.x < 0 )
298 m_FmtScale.x = 0;
299
300 if( m_FmtScale.x > 7 )
301 m_FmtScale.x = 7;
302 }
303 else
304 {
305 y_fmt_known = true;
306 m_FmtScale.y = *aText - '0';
307 m_FmtLen.y = ctmp + m_FmtScale.y;
308
309 if( m_FmtScale.y < 0 )
310 m_FmtScale.y = 0;
311
312 if( m_FmtScale.y > 7 )
313 m_FmtScale.y = 7;
314 }
315
316 aText++;
317 }
318 break;
319
320 case '*':
321 break;
322
323 default:
324 msg.Printf( wxT( "Unknown id (%c) in FS command" ),
325 *aText );
326 AddMessageToList( msg );
327 GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
328 ok = false;
329 break;
330 }
331 }
332
333 if( !x_fmt_known || !y_fmt_known )
334 AddMessageToList( wxT( "RS274X: Format Statement (FS) without X or Y format" ) );
335
336 break;
337
338 case AXIS_SELECT: // command ASAXBY*% or %ASAYBX*%
339 m_SwapAxis = false;
340
341 if( strncasecmp( aText, "AYBX", 4 ) == 0 )
342 m_SwapAxis = true;
343
344 break;
345
346 case MIRROR_IMAGE: // command %MIA0B0*%, %MIA0B1*%, %MIA1B0*%, %MIA1B1*%
347 m_MirrorA = m_MirrorB = false;
348
349 while( *aText && *aText != '*' )
350 {
351 switch( *aText )
352 {
353 case 'A': // Mirror A axis ?
354 aText++;
355
356 if( *aText == '1' )
357 m_MirrorA = true;
358
359 break;
360
361 case 'B': // Mirror B axis ?
362 aText++;
363
364 if( *aText == '1' )
365 m_MirrorB = true;
366
367 break;
368
369 default:
370 aText++;
371 break;
372 }
373 }
374 break;
375
376 case MODE_OF_UNITS:
377 code = ReadXCommandID( aText );
378
379 if( code == INCH )
380 m_GerbMetric = false;
381 else if( code == MILLIMETER )
382 m_GerbMetric = true;
383
385 break;
386
387 case FILE_ATTRIBUTE: // Command %TF ...
388 {
390 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
391
392 if( dummy.IsFileFunction() )
393 {
394 delete m_FileFunction;
396
397 // Don't set this until we get a file function; other code expects m_IsX2_file == true
398 // to mean that we have a valid m_FileFunction
399 m_IsX2_file = true;
400 }
401 else if( dummy.IsFileMD5() )
402 {
403 m_MD5_value = dummy.GetPrm( 1 );
404 }
405 else if( dummy.IsFilePart() )
406 {
407 m_PartString = dummy.GetPrm( 1 );
408 }
409 }
410 break;
411
412 case APERTURE_ATTRIBUTE: // Command %TA
413 {
415 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
416
417 if( dummy.GetAttribute() == wxT( ".AperFunction" ) )
418 {
419 m_AperFunction = dummy.GetPrm( 1 );
420
421 // A few function values can have other parameters. Add them
422 for( int ii = 2; ii < dummy.GetPrmCount(); ii++ )
423 m_AperFunction << wxT( "," ) << dummy.GetPrm( ii );
424 }
425 }
426 break;
427
428 case NET_ATTRIBUTE: // Command %TO currently %TO.P %TO.N and %TO.C
429 {
431
432 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
433
434 if( dummy.GetAttribute() == wxT( ".N" ) )
435 {
438 }
439 else if( dummy.GetAttribute() == wxT( ".C" ) )
440 {
443 }
444 else if( dummy.GetAttribute() == wxT( ".P" ) )
445 {
449
450 if( dummy.GetPrmCount() > 3 )
451 {
453 FormatStringFromGerber( dummy.GetPrm( 3 ) ), true, true );
454 }
455 else
456 {
458 }
459 }
460 }
461 break;
462
463 case REMOVE_APERTURE_ATTRIBUTE: // Command %TD ...
464 {
466 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
468 }
469 break;
470
471 case OFFSET: // command: OFAnnBnn (nn = float number) = layer Offset
472 m_Offset.x = m_Offset.y = 0;
473 while( *aText != '*' )
474 {
475 switch( *aText )
476 {
477 case 'A': // A axis offset in current unit (inch or mm)
478 aText++;
479 fcoord = ReadDouble( aText );
480 m_Offset.x = KiROUND( fcoord * conv_scale );
481 break;
482
483 case 'B': // B axis offset in current unit (inch or mm)
484 aText++;
485 fcoord = ReadDouble( aText );
486 m_Offset.y = KiROUND( fcoord * conv_scale );
487 break;
488 }
489 }
490 break;
491
492 case SCALE_FACTOR:
493 m_Scale.x = m_Scale.y = 1.0;
494 while( *aText != '*' )
495 {
496 switch( *aText )
497 {
498 case 'A': // A axis scale
499 aText++;
500 m_Scale.x = ReadDouble( aText );
501 break;
502
503 case 'B': // B axis scale
504 aText++;
505 m_Scale.y = ReadDouble( aText );
506 break;
507 }
508 }
509 break;
510
511 case IMAGE_OFFSET: // command: IOAnnBnn (nn = float number) = Image Offset
513 while( *aText != '*' )
514 {
515 switch( *aText )
516 {
517 case 'A': // A axis offset in current unit (inch or mm)
518 aText++;
519 fcoord = ReadDouble( aText );
520 m_ImageOffset.x = KiROUND( fcoord * conv_scale );
521 break;
522
523 case 'B': // B axis offset in current unit (inch or mm)
524 aText++;
525 fcoord = ReadDouble( aText );
526 m_ImageOffset.y = KiROUND( fcoord * conv_scale );
527 break;
528 }
529 }
530 break;
531
532 case IMAGE_ROTATION: // command IR0* or IR90* or IR180* or IR270*
533 if( strncasecmp( aText, "0*", 2 ) == 0 )
534 m_ImageRotation = 0;
535 else if( strncasecmp( aText, "90*", 3 ) == 0 )
536 m_ImageRotation = 90;
537 else if( strncasecmp( aText, "180*", 4 ) == 0 )
538 m_ImageRotation = 180;
539 else if( strncasecmp( aText, "270*", 4 ) == 0 )
540 m_ImageRotation = 270;
541 else
542 AddMessageToList( _( "RS274X: Command \"IR\" rotation value not allowed" ) );
543
544 break;
545
546 case STEP_AND_REPEAT: // command SR, like %SRX3Y2I5.0J2*%
547 m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
549 GetLayerParams().m_StepForRepeat.x = 0.0; // offset for Step and Repeat command
551 GetLayerParams().m_YRepeatCount = 1; // The repeat count
553
554 while( *aText && *aText != '*' )
555 {
556 switch( *aText )
557 {
558 case 'I': // X axis offset
559 aText++;
561 break;
562
563 case 'J': // Y axis offset
564 aText++;
566 break;
567
568 case 'X': // X axis repeat count
569 aText++;
571 break;
572
573 case 'Y': // Y axis offset
574 aText++;
576 break;
577
578 default:
579 aText++;
580 break;
581 }
582 }
583 break;
584
585 case IMAGE_JUSTIFY: // Command IJAnBn*
586 m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false)
587 m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false)
588 m_ImageJustifyOffset = VECTOR2I( 0, 0 ); // Image Justify Offset on XY axis (default = 0,0)
589 while( *aText && *aText != '*' )
590 {
591 // IJ command is (for A or B axis) AC or AL or A<coordinate>
592 switch( *aText )
593 {
594 case 'A': // A axis justify
595 aText++;
596
597 if( *aText == 'C' )
598 {
600 aText++;
601 }
602 else if( *aText == 'L' )
603 {
605 aText++;
606 }
607 else
608 {
609 m_ImageJustifyOffset.x = KiROUND( ReadDouble( aText ) * conv_scale);
610 }
611
612 break;
613
614 case 'B': // B axis justify
615 aText++;
616
617 if( *aText == 'C' )
618 {
620 aText++;
621 }
622 else if( *aText == 'L' )
623 {
625 aText++;
626 }
627 else
628 {
629 m_ImageJustifyOffset.y = KiROUND( ReadDouble( aText ) * conv_scale);
630 }
631
632 break;
633
634 default:
635 aText++;
636 break;
637 }
638 }
639
642
645
646 break;
647
648 case KNOCKOUT:
649 m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
650 msg = _( "RS274X: Command KNOCKOUT ignored by GerbView" ) ;
651 AddMessageToList( msg );
652 break;
653
654 case ROTATE: // Layer rotation: command like %RO45*%
655 m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
656 m_LocalRotation = ReadDouble( aText ); // Store layer rotation in degrees
657 break;
658
659 case IMAGE_NAME:
660 m_ImageName.Empty();
661
662 while( *aText != '*' )
663 m_ImageName.Append( *aText++ );
664
665 break;
666
667 case LOAD_NAME:
668 // %LN is a (deprecated) equivalentto G04: a comment
669 while( *aText && *aText != '*' )
670 aText++; // Skip text
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
680 break;
681
682 case LOAD_POLARITY:
683 if( *aText == 'C' )
685 else
687
688 break;
689
690 case AP_MACRO: // lines like %AMMYMACRO*
691 // 5,1,8,0,0,1.08239X$1,22.5*
692 // %
693 /*ok = */ReadApertureMacro( aBuff, aBuffSize, aText, m_Current_File );
694 break;
695
696 case AP_DEFINITION:
697 /* input example: %ADD30R,0.081800X0.101500*%
698 * Aperture definition has 4 options: C, R, O, P
699 * (Circle, Rect, Oval, regular Polygon)
700 * and shapes can have a hole (round or rectangular).
701 * All optional parameters values start by X
702 * at this point, text points to 2nd 'D'
703 */
704 if( *aText++ != 'D' )
705 {
706 ok = false;
707 break;
708 }
709
710 m_Has_DCode = true;
711
712 code = ReadInt( aText );
713
714 D_CODE* dcode;
715 dcode = GetDCODEOrCreate( code );
716
717 if( dcode == nullptr )
718 break;
719
721
722 // at this point, text points to character after the ADD<num>,
723 // i.e. R in example above. If aText[0] is one of the usual
724 // apertures: (C,R,O,P), there is a comma after it.
725 if( aText[1] == ',' )
726 {
727 char stdAperture = *aText;
728
729 aText += 2; // skip "C," for example
730
731 // First parameter is the size X:
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_ApertType = 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_ApertType = (stdAperture == 'O') ? APT_OVAL : APT_RECT;
767
768 while( *aText == ' ' )
769 aText++;
770
771 if( *aText == 'X' ) // Second parameter: size Y
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' ) // third parameter: drill size (or drill size 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' ) // fourth parameter: drill size Y
793 {
794 aText++;
795 dcode->m_Drill.y =
796 KiROUND( ReadDouble( aText ) * conv_scale );
798 }
799
800 dcode->m_Defined = true;
801 break;
802
803 case 'P':
804
805 /* Regular polygon: a command line like %ADD12P,0.040X10X25X0.025X0.025X0.0150*%
806 * params are: <diameter>, X<edge count>, X<Rotation>, X<X hole dim>, X<Y hole dim>
807 */
808 dcode->m_ApertType = APT_POLYGON;
809 while( *aText == ' ' )
810 aText++;
811
812 if( *aText == 'X' )
813 {
814 aText++;
815 dcode->m_EdgesCount = ReadInt( aText );
816 }
817
818 while( *aText == ' ' )
819 aText++;
820
821 if( *aText == 'X' )
822 {
823 aText++;
824 dcode->m_Rotation = EDA_ANGLE( ReadDouble( aText ), DEGREES_T );
825 }
826
827 while( *aText == ' ' )
828 aText++;
829
830 if( *aText == 'X' )
831 {
832 aText++;
833 dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
834 dcode->m_Drill.y = dcode->m_Drill.x;
836 }
837
838 while( *aText == ' ' )
839 aText++;
840
841 if( *aText == 'X' )
842 {
843 aText++;
844 dcode->m_Drill.y = KiROUND( ReadDouble( aText ) * conv_scale );
846 }
847 dcode->m_Defined = true;
848 break;
849 }
850 }
851 else // aText[0] starts an aperture macro name
852 {
853 APERTURE_MACRO am_lookup;
854
855 while( *aText && *aText != '*' && *aText != ',' )
856 am_lookup.m_AmName.Append( *aText++ );
857
858 // When an aperture definition is like %AMLINE2* 22,1,$1,$2,0,0,-45*
859 // the ADDxx<MACRO_NAME> command has parameters, like %ADD14LINE2,0.8X0.5*%
860 if( *aText == ',' )
861 { // Read aperture macro parameters and store them
862 aText++; // aText points the first parameter
863
864 while( *aText && *aText != '*' )
865 {
866 double param = ReadDouble( aText );
867 dcode->AppendParam( param );
868
869 if( !( isspace( *aText ) || *aText == 'X' || *aText == 'x' || *aText == '*' ) )
870 {
871 msg.Printf( wxT( "RS274X: aperture macro %s has invalid template "
872 "parameters\n" ),
873 TO_UTF8( am_lookup.m_AmName ) );
874 AddMessageToList( msg );
875 ok = false;
876 break;
877 }
878
879 while( isspace( *aText ) )
880 aText++;
881
882 // Skip 'X' separator:
883 if( *aText == 'X' || *aText == 'x' )
884 aText++;
885 }
886 }
887
888 // lookup the aperture macro here.
889 APERTURE_MACRO* pam = FindApertureMacro( am_lookup );
890
891 if( !pam )
892 {
893 msg.Printf( wxT( "RS274X: aperture macro %s not found\n" ),
894 TO_UTF8( am_lookup.m_AmName ) );
895 AddMessageToList( msg );
896 ok = false;
897 break;
898 }
899
900 dcode->m_ApertType = APT_MACRO;
901 dcode->SetMacro( pam );
902 dcode->m_Defined = true;
903 }
904
905 break;
906
907 default:
908 ok = false;
909 break;
910 }
911
912 ignore_unused( seq_len );
913
914 ok = GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
915
916 return ok;
917}
918
919
920bool GERBER_FILE_IMAGE::GetEndOfBlock( char* aBuff, unsigned int aBuffSize, char*& aText, FILE* gerber_file )
921{
922 for( ; ; )
923 {
924 while( (aText < aBuff + aBuffSize) && *aText )
925 {
926 if( *aText == '*' )
927 return true;
928
929 if( *aText == '%' )
930 return true;
931
932 aText++;
933 }
934
935 if( fgets( aBuff, aBuffSize, gerber_file ) == nullptr )
936 break;
937
938 m_LineNum++;
939 aText = aBuff;
940 }
941
942 return false;
943}
944
945
946char* GERBER_FILE_IMAGE::GetNextLine( char *aBuff, unsigned int aBuffSize, char* aText, FILE* aFile )
947{
948 for( ; ; )
949 {
950 switch (*aText )
951 {
952 case ' ': // skip blanks
953 case '\n':
954 case '\r': // Skip line terminators
955 ++aText;
956 break;
957
958 case 0: // End of text found in aBuff: Read a new string
959 if( fgets( aBuff, aBuffSize, aFile ) == nullptr )
960 return nullptr;
961
962 m_LineNum++;
963 aText = aBuff;
964 return aText;
965
966 default:
967 return aText;
968 }
969 }
970 return aText;
971}
972
973
974bool GERBER_FILE_IMAGE::ReadApertureMacro( char *aBuff, unsigned int aBuffSize,
975 char*& aText,
976 FILE* gerber_file )
977{
978 wxString msg;
980
981 // read macro name
982 while( *aText )
983 {
984 if( *aText == '*' )
985 {
986 ++aText;
987 break;
988 }
989
990 am.m_AmName.Append( *aText++ );
991 }
992
993 // Read aperture macro parameters
994 for( ; ; )
995 {
996 if( *aText == '*' )
997 ++aText;
998
999 aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1000
1001 if( aText == nullptr ) // End of File
1002 return false;
1003
1004 // aText points the beginning of a new line.
1005
1006 // Test for the last line in aperture macro lis:
1007 // last line is % or *% sometime found.
1008 if( *aText == '*' )
1009 ++aText;
1010
1011 if( *aText == '%' )
1012 break; // exit with aText still pointing at %
1013
1014 int paramCount = 0; // will be set to the minimal parameters count,
1015 // depending on the actual primitive
1016 int primitive_type = AMP_UNKNOWN;
1017 // Test for a valid symbol at the beginning of a description:
1018 // it can be: a parameter declaration like $1=$2/4
1019 // or a digit (macro primitive selection)
1020 // all other symbols are illegal.
1021 if( *aText == '$' ) // local parameter declaration, inside the aperture macro
1022 {
1025 aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1026
1027 if( aText == nullptr) // End of File
1028 return false;
1029
1030 param.ReadParamFromAmDef( aText );
1031 continue;
1032 }
1033 else if( !isdigit(*aText) ) // Ill. symbol
1034 {
1035 msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": ill. symbol, line: \"%s\"" ),
1036 am.m_AmName, FROM_UTF8( aBuff ) );
1037 AddMessageToList( msg );
1038 primitive_type = AMP_COMMENT;
1039 }
1040 else
1041 primitive_type = ReadInt( aText );
1042
1043 bool is_comment = false;
1044
1045 switch( primitive_type )
1046 {
1047 case AMP_COMMENT: // lines starting by 0 are a comment
1048 paramCount = 0;
1049 is_comment = true;
1050
1051 // Skip comment
1052 while( *aText && ( *aText != '*' ) )
1053 aText++;
1054
1055 break;
1056
1057 case AMP_CIRCLE:
1058 paramCount = 4; // minimal count. can have a optional parameter (rotation)
1059 break;
1060
1061 case AMP_LINE2:
1062 case AMP_LINE20:
1063 paramCount = 7;
1064 break;
1065
1066 case AMP_LINE_CENTER:
1068 paramCount = 6;
1069 break;
1070
1071 case AMP_OUTLINE:
1072 paramCount = 4; // partial count. other parameters are vertices and rotation
1073 // Second parameter is vertice (coordinate pairs) count.
1074 break;
1075
1076 case AMP_POLYGON:
1077 paramCount = 6;
1078 break;
1079
1080 case AMP_MOIRE:
1081 paramCount = 9;
1082 break;
1083
1084 case AMP_THERMAL:
1085 paramCount = 6;
1086 break;
1087
1088 default:
1089 msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Invalid primitive id code %d, line %d: \"%s\"" ),
1090 am.m_AmName, primitive_type, m_LineNum, FROM_UTF8( aBuff ) );
1091 AddMessageToList( msg );
1092 return false;
1093 }
1094
1095 if( is_comment )
1096 continue;
1097
1098 AM_PRIMITIVE prim( m_GerbMetric );
1099 prim.m_Primitive_id = (AM_PRIMITIVE_ID) primitive_type;
1100 int ii;
1101
1102 for( ii = 0; ii < paramCount && *aText && *aText != '*'; ++ii )
1103 {
1104 prim.m_Params.push_back( AM_PARAM() );
1105
1106 AM_PARAM& param = prim.m_Params.back();
1107
1108 aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1109
1110 if( aText == nullptr) // End of File
1111 return false;
1112
1113 param.ReadParamFromAmDef( aText );
1114 }
1115
1116 if( ii < paramCount )
1117 {
1118 // maybe some day we can throw an exception and track a line number
1119 msg.Printf( wxT( "RS274X: read macro descr type %d: read %d parameters, insufficient "
1120 "parameters\n" ),
1121 prim.m_Primitive_id, ii );
1122 AddMessageToList( msg );
1123 }
1124
1125 // there are more parameters to read if this is an AMP_OUTLINE
1126 if( prim.m_Primitive_id == AMP_OUTLINE )
1127 {
1128 // so far we have read [0]:exposure, [1]:#points, [2]:X start, [3]: Y start
1129 // Now read all the points, plus trailing rotation in degrees.
1130
1131 // m_Params[1] is a count of polygon points, so it must be given
1132 // in advance, i.e. be immediate.
1133 wxASSERT( prim.m_Params[1].IsImmediate() );
1134
1135 paramCount = (int) prim.m_Params[1].GetValueFromMacro( nullptr ) * 2 + 1;
1136
1137 for( int jj = 0; jj < paramCount && *aText != '*'; ++jj )
1138 {
1139 prim.m_Params.push_back( AM_PARAM() );
1140
1141 AM_PARAM& param = prim.m_Params.back();
1142
1143 aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1144
1145 if( aText == nullptr ) // End of File
1146 return false;
1147
1148 param.ReadParamFromAmDef( aText );
1149 }
1150 }
1151
1152 // AMP_CIRCLE can have a optional parameter (rotation)
1153 if( prim.m_Primitive_id == AMP_CIRCLE && aText && *aText != '*' )
1154 {
1155 prim.m_Params.push_back( AM_PARAM() );
1156 AM_PARAM& param = prim.m_Params.back();
1157 param.ReadParamFromAmDef( aText );
1158 }
1159
1160 // The primitive description is now parsed: push it to the current aperture macro
1161 am.AddPrimitiveToList( prim );
1162 }
1163
1164 m_aperture_macros.insert( am );
1165
1166 return true;
1167}
1168
AM_PRIMITIVE_ID
The set of all "aperture macro primitives" (primitive numbers).
Definition: am_primitive.h:69
@ AMP_POLYGON
Definition: am_primitive.h:79
@ AMP_LINE_LOWER_LEFT
Definition: am_primitive.h:77
@ AMP_LINE2
Definition: am_primitive.h:74
@ AMP_CIRCLE
Definition: am_primitive.h:73
@ AMP_THERMAL
Definition: am_primitive.h:82
@ AMP_UNKNOWN
Definition: am_primitive.h:70
@ AMP_COMMENT
Definition: am_primitive.h:71
@ AMP_LINE_CENTER
Definition: am_primitive.h:76
@ AMP_MOIRE
Definition: am_primitive.h:81
@ AMP_OUTLINE
Definition: am_primitive.h:78
@ AMP_LINE20
Definition: am_primitive.h:75
constexpr EDA_IU_SCALE gerbIUScale
Definition: base_units.h:108
Hold a parameter value for an "aperture macro" as defined within standard RS274X.
Definition: am_param.h:285
bool ReadParamFromAmDef(char *&aText)
Read one aperture macro parameter.
Definition: am_param.cpp:168
An aperture macro primitive as given in gerber layer format doc.
Definition: am_primitive.h:92
AM_PARAMS m_Params
A sequence of parameters used by the primitive.
Definition: am_primitive.h:95
AM_PRIMITIVE_ID m_Primitive_id
The primitive type.
Definition: am_primitive.h:94
Support the "aperture macro" defined within standard RS274X.
wxString m_AmName
The name of the aperture macro as defined like AMVB_RECTANGLE* (name is VB_RECTANGLE)
void AddLocalParamDefToStack()
A deferred parameter can be defined in aperture macro, but outside aperture primitives.
AM_PARAM & GetLastLocalParamDefFromStack()
void AddPrimitiveToList(AM_PRIMITIVE &aPrimitive)
Add a new ptimitive ( AMP_CIRCLE, AMP_LINE2 ...) to the list of primitives to define the full shape o...
A gerber DCODE (also called Aperture) definition.
Definition: dcode.h:80
wxString m_AperFunction
the aperture attribute (created by a TA.AperFunction command).
Definition: dcode.h:203
EDA_ANGLE m_Rotation
shape rotation
Definition: dcode.h:197
VECTOR2I m_Drill
dimension of the hole (if any) (drill file)
Definition: dcode.h:194
void SetMacro(APERTURE_MACRO *aMacro)
Definition: dcode.h:120
int m_EdgesCount
in aperture definition Polygon only: number of edges for the polygon
Definition: dcode.h:198
VECTOR2I m_Size
Horizontal and vertical dimensions.
Definition: dcode.h:190
APERTURE_T m_ApertType
Aperture type ( Line, rectangle, circle, oval poly, macro )
Definition: dcode.h:191
bool m_Defined
false if the aperture is not defined in the header
Definition: dcode.h:202
APERTURE_DEF_HOLETYPE m_DrillShape
shape of the hole (0 = no hole, round = 1, rect = 2).
Definition: dcode.h:195
void AppendParam(double aValue)
Add a parameter to the D_CODE parameter list.
Definition: dcode.h:91
void SetField(const wxString &aField, bool aUseUTF8, bool aEscapeString)
int m_NetAttribType
the type of net info (used to define the gerber string to create)
wxString m_Cmpref
the component reference parent of the data
@ GBR_NETINFO_NET
print info associated to a net (TO.N attribute)
@ GBR_NETINFO_CMP
print info associated to a component (TO.C attribute)
@ GBR_NETINFO_PAD
print info associated to a flashed pad (TO.P attribute)
GBR_DATA_FIELD m_PadPinFunction
for a pad: the pin function (defined in schematic)
GBR_DATA_FIELD m_Padname
for a flashed pad: the pad name ((TO.P attribute)
wxString m_Netname
for items associated to a net: the netname
APERTURE_MACRO * FindApertureMacro(const APERTURE_MACRO &aLookup)
Look up a previously read in aperture macro.
X2_ATTRIBUTE_FILEFUNCTION * m_FileFunction
file function parameters, found in a TF command or a G04
void RemoveAttribute(X2_ATTRIBUTE &aAttribute)
Called when a TD command is found the Gerber file.
bool m_SwapAxis
false if A = X and B = Y (default); true if A = Y, B = X
double m_LocalRotation
Local rotation added to m_ImageRotation.
VECTOR2I m_Offset
Coord Offset, from OF command.
wxSize m_FmtScale
Fmt 2.3: m_FmtScale = 3, fmt 3.4: m_FmtScale = 4.
wxString m_FileName
Full File Name for this layer.
bool m_ImageJustifyXCenter
Image Justify Center on X axis (default = false)
bool m_ImageJustifyYCenter
Image Justify Center on Y axis (default = false)
bool m_ImageNegative
true = Negative image
void AddMessageToList(const wxString &aMessage)
Add a message to the message list.
int m_LineNum
Line number of the gerber file while reading.
wxString m_ImageName
Image name, from IN <name>* command.
bool m_Relative
false = absolute Coord, true = relative Coord.
wxString m_MD5_value
MD5 value found in a TF.MD5 command.
char * GetNextLine(char *aBuff, unsigned int aBuffSize, char *aText, FILE *aFile)
Test for an end of line.
Definition: rs274x.cpp:946
bool m_MirrorB
true: mirror / axis B (Y)
wxString m_PartString
string found in a TF.Part command
bool ReadRS274XCommand(char *aBuff, unsigned int aBuffSize, char *&aText)
Read a single RS274X command terminated with a %.
Definition: rs274x.cpp:144
bool m_IsX2_file
True if a X2 gerber attribute was found in file.
int m_ImageRotation
Image rotation (0, 90, 180, 270 only) in degrees.
VECTOR2I m_ImageOffset
Coord Offset, from IO command.
bool m_Has_DCode
< True if has DCodes in file or false if no DCodes found. Perhaps deprecated RS274D file.
bool GetEndOfBlock(char *aBuff, unsigned int aBuffSize, char *&aText, FILE *aGerberFile)
Definition: rs274x.cpp:920
bool ReadApertureMacro(char *aBuff, unsigned int aBuffSize, char *&text, FILE *gerber_file)
Read in an aperture macro and saves it in m_aperture_macros.
Definition: rs274x.cpp:974
D_CODE * GetDCODEOrCreate(int aDCODE, bool aCreateIfNoExist=true)
Return a pointer to the D_CODE within this GERBER for the given aDCODE.
bool ExecuteRS274XCommand(int aCommand, char *aBuff, unsigned int aBuffSize, char *&aText)
Execute a RS274X command.
Definition: rs274x.cpp:199
wxSize m_FmtLen
Nb chars per coord. ex fmt 2.3, m_FmtLen = 5.
APERTURE_MACRO_SET m_aperture_macros
int ReadXCommandID(char *&text)
Read two bytes of data and assembles them into an int with the first byte in the sequence put into th...
Definition: rs274x.cpp:116
bool m_GerbMetric
false = Inches, true = metric
GBR_NETLIST_METADATA m_NetAttributeDict
int m_CommandState
state of gerber analysis command
bool m_NoTrailingZeros
true: remove tailing zeros.
int m_Iterpolation
Linear, 90 arc, Circ.
bool m_MirrorA
true: mirror / axis A (X)
VECTOR2I m_Scale
scale (X and Y) of layer.
VECTOR2I m_ImageJustifyOffset
Image Justify Offset on XY axis (default = 0,0)
GERBER_LAYER & GetLayerParams()
wxRealPoint m_StepForRepeat
bool m_StepForRepeatMetric
X2_ATTRIBUTE_FILEFUNCTION ( from TF.FileFunction in Gerber file) Example file function: TF....
The attribute value consists of a number of substrings separated by a comma.
@ APT_DEF_ROUND_HOLE
Definition: dcode.h:62
@ APT_DEF_RECT_HOLE
Definition: dcode.h:63
@ APT_RECT
Definition: dcode.h:50
@ APT_OVAL
Definition: dcode.h:51
@ APT_POLYGON
Definition: dcode.h:52
@ APT_CIRCLE
Definition: dcode.h:49
@ APT_MACRO
Definition: dcode.h:54
#define _(s)
@ DEGREES_T
Definition: eda_angle.h:31
wxString FormatStringFromGerber(const wxString &aString)
Convert a gerber string into a 16 bit Unicode string.
Handle special data (items attributes) during plot.
@ GERB_INTERPOL_LINEAR_1X
Definition: gerbview.h:35
@ CMD_IDLE
Definition: gerbview.h:64
void ignore_unused(const T &)
Definition: ignore.h:24
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
RS274X_PARAMETERS
Definition: rs274x.cpp:57
@ FILE_ATTRIBUTE
Definition: rs274x.cpp:87
@ IMAGE_JUSTIFY
Definition: rs274x.cpp:71
@ APERTURE_ATTRIBUTE
Definition: rs274x.cpp:95
@ FORMAT_STATEMENT
Definition: rs274x.cpp:61
@ NET_ATTRIBUTE
Definition: rs274x.cpp:92
@ IMAGE_OFFSET
Definition: rs274x.cpp:73
@ ROTATE
Definition: rs274x.cpp:109
@ STEP_AND_REPEAT
Definition: rs274x.cpp:108
@ LOAD_NAME
Definition: rs274x.cpp:112
@ REMOVE_APERTURE_ATTRIBUTE
Definition: rs274x.cpp:101
@ KNOCKOUT
Definition: rs274x.cpp:107
@ AP_MACRO
Definition: rs274x.cpp:80
@ MIRROR_IMAGE
Definition: rs274x.cpp:62
@ MODE_OF_UNITS
Definition: rs274x.cpp:63
@ INCH
Definition: rs274x.cpp:64
@ IMAGE_NAME
Definition: rs274x.cpp:72
@ OFFSET
Definition: rs274x.cpp:66
@ MILLIMETER
Definition: rs274x.cpp:65
@ AXIS_SELECT
Definition: rs274x.cpp:60
@ SCALE_FACTOR
Definition: rs274x.cpp:67
@ IMAGE_ROTATION
Definition: rs274x.cpp:75
@ LOAD_POLARITY
Definition: rs274x.cpp:111
@ IMAGE_POLARITY
Definition: rs274x.cpp:74
@ AP_DEFINITION
Definition: rs274x.cpp:79
#define CODE(x, y)
Definition: rs274x.cpp:44
int ReadInt(char *&text, bool aSkipSeparator=true)
Read an integer from an ASCII character buffer.
double ReadDouble(char *&text, bool aSkipSeparator=true)
Read a double precision floating point number from an ASCII character buffer.
std::vector< FAB_LAYER_COLOR > dummy
const double IU_PER_MILS
Definition: base_units.h:78
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:590