KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 <core/ignore.h>
35#include <macros.h>
36#include <string_utils.h>
38#include <gbr_metadata.h>
39#include <wx/log.h>
40
41extern int ReadInt( char*& text, bool aSkipSeparator = true );
42extern double ReadDouble( char*& text, bool aSkipSeparator = true );
43
44
45#define CODE( x, y ) ( ( (x) << 8 ) + (y) )
46
47// See rs274xrevd_e.pdf, table 1: RS-274X parameters order of entry
48// in gerber files, when a coordinate is given (like X78Y600 or I0J80):
49// Y and Y are logical coordinates
50// A and B are plotter coordinates
51// Usually A = X, B = Y
52// But we can have A = Y, B = X and/or offset, mirror, scale;
53// Also:
54// Image is what you must plot (the entire data of the file).
55// Layer is just a set of data blocks with their parameters. An image can have more than one
56// layer so a gerber layer is not like a board layer or the graphic layers used in GerbView
57// to show a file.
59 // Directive parameters: single usage recommended
60 // Must be at the beginning of the file
61 AXIS_SELECT = CODE( 'A', 'S' ), // Default: A=X, B=Y
62 FORMAT_STATEMENT = CODE( 'F', 'S' ), // no default: this command must exists
63 MIRROR_IMAGE = CODE( 'M', 'I' ), // Default: mo mirror
64 MODE_OF_UNITS = CODE( 'M', 'O' ), // Default: inch
65 INCH = CODE( 'I', 'N' ),
66 MILLIMETER = CODE( 'M', 'M' ),
67 OFFSET = CODE( 'O', 'F' ), // Default: A = 0, B = 0
68 SCALE_FACTOR = CODE( 'S', 'F' ), // Default: A = 1.0, B = 1.0
69
70 // Image parameters:
71 // commands used only once at the beginning of the file, and are deprecated
72 IMAGE_JUSTIFY = CODE( 'I', 'J' ), // Default: no justification
73 IMAGE_NAME = CODE( 'I', 'N' ), // Default: void
74 IMAGE_OFFSET = CODE( 'I', 'O' ), // Default: A = 0, B = 0
75 IMAGE_POLARITY = CODE( 'I', 'P' ), // Default: Positive
76 IMAGE_ROTATION = CODE( 'I', 'R' ), // Default: 0
77
78 // Aperture parameters:
79 // Usually for the whole file
80 AP_DEFINITION = CODE( 'A', 'D' ),
81 AP_MACRO = CODE( 'A', 'M' ),
82
83 // X2 extension attribute commands
84 // Mainly are found standard attributes and user attributes
85 // standard attributes commands are:
86 // TF (file attribute) TO (net attribute)
87 // TA (aperture attribute) and TD (delete aperture attribute)
88 FILE_ATTRIBUTE = CODE( 'T', 'F' ),
89
90 // X2 extension Net attribute info
91 // Net attribute options are:
92 // TO (net attribute data): TO.CN or TO.P TO.N or TO.C
93 NET_ATTRIBUTE = CODE( 'T', 'O' ),
94
95 // X2 extension Aperture attribute TA
96 APERTURE_ATTRIBUTE = CODE( 'T', 'A' ),
97
98 // TD (delete aperture/object attribute):
99 // Delete aperture attribute added by %TA or Oblect attribute added b %TO
100 // TD (delete all) or %TD<attr name> to delete <attr name>.
101 // eg: TD.P or TD.N or TD.C ...
103
104 // Layer specific parameters
105 // May be used singly or may be layer specific
106 // These parameters are at the beginning of the file or layer
107 // and reset some layer parameters (like interpolation)
108 KNOCKOUT = CODE( 'K', 'O' ), // Default: off
109 STEP_AND_REPEAT = CODE( 'S', 'R' ), // Default: A = 1, B = 1
110 ROTATE = CODE( 'R', 'O' ), // Default: 0
111
112 LOAD_POLARITY = CODE( 'L', 'P' ), //LPC or LPD. Default: Dark (LPD)
113 LOAD_NAME = CODE( 'L', 'N' ), // Deprecated: equivalent to G04
114};
115
116
118{
119 /* reads two bytes of data and assembles them into an int with the first
120 * byte in the sequence put into the most significant part of a 16 bit value
121 */
122 int result;
123 int currbyte;
124
125 if( text && *text )
126 {
127 currbyte = *text++;
128 result = ( currbyte & 0xFF ) << 8;
129 }
130 else
131 return -1;
132
133 if( text && *text )
134 {
135 currbyte = *text++;
136 result += currbyte & 0xFF;
137 }
138 else
139 return -1;
140
141 return result;
142}
143
144
145bool GERBER_FILE_IMAGE::ReadRS274XCommand( char *aBuff, unsigned int aBuffSize, char*& aText )
146{
147 bool ok = true;
148 int code_command;
149
150 aText++;
151
152 for( ; ; )
153 {
154 while( *aText )
155 {
156 switch( *aText )
157 {
158 case '%': // end of command
159 aText++;
161 goto exit; // success completion
162
163 case ' ':
164 case '\r':
165 case '\n':
166 aText++;
167 break;
168
169 case '*':
170 aText++;
171 break;
172
173 default:
174 code_command = ReadXCommandID( aText );
175 ok = ExecuteRS274XCommand( code_command, aBuff, aBuffSize, aText );
176
177 if( !ok )
178 goto exit;
179
180 break;
181 }
182 }
183
184 // end of current line, read another one.
185 if( fgets( aBuff, aBuffSize, m_Current_File ) == nullptr )
186 {
187 // end of file
188 ok = false;
189 break;
190 }
191 m_LineNum++;
192 aText = aBuff;
193 }
194
195exit:
196 return ok;
197}
198
199
200bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff,
201 unsigned int aBuffSize, char*& aText )
202{
203 int code;
204 int seq_len; // not used, just provided
205 int seq_char;
206 bool ok = true;
207 wxString msg;
208 double fcoord;
209 bool x_fmt_known = false;
210 bool y_fmt_known = false;
211
212 // conv_scale = scaling factor from inch to Internal Unit
213 double conv_scale = gerbIUScale.IU_PER_MILS * 1000;
214
215 if( m_GerbMetric )
216 conv_scale /= 25.4;
217
218 switch( aCommand )
219 {
220 case FORMAT_STATEMENT:
221 seq_len = 2;
222
223 while( *aText != '*' )
224 {
225 switch( *aText )
226 {
227 case ' ':
228 aText++;
229 break;
230
231 case 'D': // Non-standard option for all zeros (leading + tailing)
232 msg.Printf( _( "RS274X: Invalid GERBER format command '%c' at line %d: \"%s\"" ),
233 'D', m_LineNum, aBuff );
234 AddMessageToList( msg );
235 msg.Printf( _("GERBER file \"%s\" may not display as intended." ),
236 m_FileName.ToAscii() );
237 AddMessageToList( msg );
239
240 case 'L': // No Leading 0
241 m_NoTrailingZeros = false;
242 aText++;
243 break;
244
245 case 'T': // No trailing 0
246 m_NoTrailingZeros = true;
247 aText++;
248 break;
249
250 case 'A': // Absolute coord
251 m_Relative = false;
252 aText++;
253 break;
254
255 case 'I': // Relative coord
256 m_Relative = true;
257 aText++;
258 break;
259
260 case 'G':
261 case 'N': // Sequence code (followed by one digit: the sequence len)
262 // (sometimes found before the X,Y sequence)
263 // Obscure option
264 aText++;
265 seq_char = *aText++;
266
267 if( (seq_char >= '0') && (seq_char <= '9') )
268 seq_len = seq_char - '0';
269
270 break;
271
272 case 'M': // Sequence code (followed by one digit: the sequence len)
273 // (sometimes found after the X,Y sequence)
274 // Obscure option
275 aText++;
276 code = *aText;
277
278 if( ( code >= '0' ) && ( code <= '9' ) )
279 aText++; // skip the digit
280
281 break;
282
283 case 'X':
284 case 'Y':
285 {
286 code = *(aText++);
287 char ctmp = *(aText++) - '0';
288
289 if( code == 'X' )
290 {
291 x_fmt_known = true;
292 // number of digits after the decimal point (0 to 7 allowed)
293 m_FmtScale.x = *aText - '0';
294 m_FmtLen.x = ctmp + m_FmtScale.x;
295
296 // m_FmtScale is 0 to 7
297 // (Old Gerber specification was 0 to 6)
298 if( m_FmtScale.x < 0 )
299 m_FmtScale.x = 0;
300
301 if( m_FmtScale.x > 7 )
302 m_FmtScale.x = 7;
303 }
304 else
305 {
306 y_fmt_known = true;
307 m_FmtScale.y = *aText - '0';
308 m_FmtLen.y = ctmp + m_FmtScale.y;
309
310 if( m_FmtScale.y < 0 )
311 m_FmtScale.y = 0;
312
313 if( m_FmtScale.y > 7 )
314 m_FmtScale.y = 7;
315 }
316
317 aText++;
318 }
319 break;
320
321 case '*':
322 break;
323
324 default:
325 msg.Printf( wxT( "Unknown id (%c) in FS command" ),
326 *aText );
327 AddMessageToList( msg );
328 GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
329 ok = false;
330 break;
331 }
332 }
333
334 if( !x_fmt_known || !y_fmt_known )
335 AddMessageToList( wxT( "RS274X: Format Statement (FS) without X or Y format" ) );
336
337 break;
338
339 case AXIS_SELECT: // command ASAXBY*% or %ASAYBX*%
340 m_SwapAxis = false;
341
342 if( strncasecmp( aText, "AYBX", 4 ) == 0 )
343 m_SwapAxis = true;
344
345 break;
346
347 case MIRROR_IMAGE: // command %MIA0B0*%, %MIA0B1*%, %MIA1B0*%, %MIA1B1*%
348 m_MirrorA = m_MirrorB = false;
349
350 while( *aText && *aText != '*' )
351 {
352 switch( *aText )
353 {
354 case 'A': // Mirror A axis ?
355 aText++;
356
357 if( *aText == '1' )
358 m_MirrorA = true;
359
360 break;
361
362 case 'B': // Mirror B axis ?
363 aText++;
364
365 if( *aText == '1' )
366 m_MirrorB = true;
367
368 break;
369
370 default:
371 aText++;
372 break;
373 }
374 }
375 break;
376
377 case MODE_OF_UNITS:
378 code = ReadXCommandID( aText );
379
380 if( code == INCH )
381 m_GerbMetric = false;
382 else if( code == MILLIMETER )
383 m_GerbMetric = true;
384
386 break;
387
388 case FILE_ATTRIBUTE: // Command %TF ...
389 {
391 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
392
393 if( dummy.IsFileFunction() )
394 {
395 delete m_FileFunction;
397
398 // Don't set this until we get a file function; other code expects m_IsX2_file == true
399 // to mean that we have a valid m_FileFunction
400 m_IsX2_file = true;
401 }
402 else if( dummy.IsFileMD5() )
403 {
404 m_MD5_value = dummy.GetPrm( 1 );
405 }
406 else if( dummy.IsFilePart() )
407 {
408 m_PartString = dummy.GetPrm( 1 );
409 }
410 }
411 break;
412
413 case APERTURE_ATTRIBUTE: // Command %TA
414 {
416 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
417
418 if( dummy.GetAttribute() == wxT( ".AperFunction" ) )
419 {
420 m_AperFunction = dummy.GetPrm( 1 );
421
422 // A few function values can have other parameters. Add them
423 for( int ii = 2; ii < dummy.GetPrmCount(); ii++ )
424 m_AperFunction << wxT( "," ) << dummy.GetPrm( ii );
425 }
426 }
427 break;
428
429 case NET_ATTRIBUTE: // Command %TO currently %TO.P %TO.N and %TO.C
430 {
432
433 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
434
435 if( dummy.GetAttribute() == wxT( ".N" ) )
436 {
439 }
440 else if( dummy.GetAttribute() == wxT( ".C" ) )
441 {
444 }
445 else if( dummy.GetAttribute() == wxT( ".P" ) )
446 {
450
451 if( dummy.GetPrmCount() > 3 )
452 {
454 FormatStringFromGerber( dummy.GetPrm( 3 ) ), true, true );
455 }
456 else
457 {
459 }
460 }
461 }
462 break;
463
464 case REMOVE_APERTURE_ATTRIBUTE: // Command %TD ...
465 {
467 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
469 }
470 break;
471
472 case OFFSET: // command: OFAnnBnn (nn = float number) = layer Offset
473 m_Offset.x = m_Offset.y = 0;
474 while( *aText != '*' )
475 {
476 switch( *aText )
477 {
478 case 'A': // A axis offset in current unit (inch or mm)
479 aText++;
480 fcoord = ReadDouble( aText );
481 m_Offset.x = KiROUND( fcoord * conv_scale );
482 break;
483
484 case 'B': // B axis offset in current unit (inch or mm)
485 aText++;
486 fcoord = ReadDouble( aText );
487 m_Offset.y = KiROUND( fcoord * conv_scale );
488 break;
489 }
490 }
491 break;
492
493 case SCALE_FACTOR:
494 m_Scale.x = m_Scale.y = 1.0;
495 while( *aText != '*' )
496 {
497 switch( *aText )
498 {
499 case 'A': // A axis scale
500 aText++;
501 m_Scale.x = ReadDouble( aText );
502 break;
503
504 case 'B': // B axis scale
505 aText++;
506 m_Scale.y = ReadDouble( aText );
507 break;
508 }
509 }
510 break;
511
512 case IMAGE_OFFSET: // command: IOAnnBnn (nn = float number) = Image Offset
514 while( *aText != '*' )
515 {
516 switch( *aText )
517 {
518 case 'A': // A axis offset in current unit (inch or mm)
519 aText++;
520 fcoord = ReadDouble( aText );
521 m_ImageOffset.x = KiROUND( fcoord * conv_scale );
522 break;
523
524 case 'B': // B axis offset in current unit (inch or mm)
525 aText++;
526 fcoord = ReadDouble( aText );
527 m_ImageOffset.y = KiROUND( fcoord * conv_scale );
528 break;
529 }
530 }
531 break;
532
533 case IMAGE_ROTATION: // command IR0* or IR90* or IR180* or IR270*
534 if( strncasecmp( aText, "0*", 2 ) == 0 )
535 m_ImageRotation = 0;
536 else if( strncasecmp( aText, "90*", 3 ) == 0 )
537 m_ImageRotation = 90;
538 else if( strncasecmp( aText, "180*", 4 ) == 0 )
539 m_ImageRotation = 180;
540 else if( strncasecmp( aText, "270*", 4 ) == 0 )
541 m_ImageRotation = 270;
542 else
543 AddMessageToList( _( "RS274X: Command \"IR\" rotation value not allowed" ) );
544
545 break;
546
547 case STEP_AND_REPEAT: // command SR, like %SRX3Y2I5.0J2*%
548 m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
550 GetLayerParams().m_StepForRepeat.x = 0.0; // offset for Step and Repeat command
552 GetLayerParams().m_YRepeatCount = 1; // The repeat count
554
555 while( *aText && *aText != '*' )
556 {
557 switch( *aText )
558 {
559 case 'I': // X axis offset
560 aText++;
562 break;
563
564 case 'J': // Y axis offset
565 aText++;
567 break;
568
569 case 'X': // X axis repeat count
570 aText++;
572 break;
573
574 case 'Y': // Y axis offset
575 aText++;
577 break;
578
579 default:
580 aText++;
581 break;
582 }
583 }
584 break;
585
586 case IMAGE_JUSTIFY: // Command IJAnBn*
587 m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false)
588 m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false)
589 m_ImageJustifyOffset = VECTOR2I( 0, 0 ); // Image Justify Offset on XY axis (default = 0,0)
590 while( *aText && *aText != '*' )
591 {
592 // IJ command is (for A or B axis) AC or AL or A<coordinate>
593 switch( *aText )
594 {
595 case 'A': // A axis justify
596 aText++;
597
598 if( *aText == 'C' )
599 {
601 aText++;
602 }
603 else if( *aText == 'L' )
604 {
606 aText++;
607 }
608 else
609 {
610 m_ImageJustifyOffset.x = KiROUND( ReadDouble( aText ) * conv_scale);
611 }
612
613 break;
614
615 case 'B': // B axis justify
616 aText++;
617
618 if( *aText == 'C' )
619 {
621 aText++;
622 }
623 else if( *aText == 'L' )
624 {
626 aText++;
627 }
628 else
629 {
630 m_ImageJustifyOffset.y = KiROUND( ReadDouble( aText ) * conv_scale);
631 }
632
633 break;
634
635 default:
636 aText++;
637 break;
638 }
639 }
640
643
646
647 break;
648
649 case KNOCKOUT:
650 m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
651 msg = _( "RS274X: Command KNOCKOUT ignored by GerbView" ) ;
652 AddMessageToList( msg );
653 break;
654
655 case ROTATE: // Layer rotation: command like %RO45*%
656 m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
657 m_LocalRotation = ReadDouble( aText ); // Store layer rotation in degrees
658 break;
659
660 case IMAGE_NAME:
661 m_ImageName.Empty();
662
663 while( *aText != '*' )
664 m_ImageName.Append( *aText++ );
665
666 break;
667
668 case LOAD_NAME:
669 // %LN is a (deprecated) equivalentto G04: a comment
670 while( *aText && *aText != '*' )
671 aText++; // Skip text
672
673 break;
674
675 case IMAGE_POLARITY:
676 if( strncasecmp( aText, "NEG", 3 ) == 0 )
677 m_ImageNegative = true;
678 else
679 m_ImageNegative = false;
680
681 break;
682
683 case LOAD_POLARITY:
684 if( *aText == 'C' )
686 else
688
689 break;
690
691 case AP_MACRO: // lines like %AMMYMACRO*
692 // 5,1,8,0,0,1.08239X$1,22.5*
693 // %
694 /*ok = */ReadApertureMacro( aBuff, aBuffSize, aText, m_Current_File );
695 break;
696
697 case AP_DEFINITION:
698 /* input example: %ADD30R,0.081800X0.101500*%
699 * Aperture definition has 4 options: C, R, O, P
700 * (Circle, Rect, Oval, regular Polygon)
701 * and shapes can have a hole (round or rectangular).
702 * All optional parameters values start by X
703 * at this point, text points to 2nd 'D'
704 */
705 if( *aText++ != 'D' )
706 {
707 ok = false;
708 break;
709 }
710
711 m_Has_DCode = true;
712
713 code = ReadInt( aText );
714
715 D_CODE* dcode;
716 dcode = GetDCODEOrCreate( code );
717
718 if( dcode == nullptr )
719 break;
720
722
723 // at this point, text points to character after the ADD<num>,
724 // i.e. R in example above. If aText[0] is one of the usual
725 // apertures: (C,R,O,P), there is a comma after it.
726 if( aText[1] == ',' )
727 {
728 char stdAperture = *aText;
729
730 aText += 2; // skip "C," for example
731
732 // First parameter is the size X:
733 dcode->m_Size.x = KiROUND( ReadDouble( aText ) * conv_scale );
734 dcode->m_Size.y = dcode->m_Size.x;
735
736 switch( stdAperture ) // Aperture desceiption has optional parameters. Read them
737 {
738 case 'C': // Circle
739 dcode->m_ApertType = APT_CIRCLE;
740 while( *aText == ' ' )
741 aText++;
742
743 if( *aText == 'X' )
744 {
745 aText++;
746 dcode->m_Drill.x = dcode->m_Drill.y =
747 KiROUND( ReadDouble( aText ) * conv_scale );
749 }
750
751 while( *aText == ' ' )
752 aText++;
753
754 if( *aText == 'X' )
755 {
756 aText++;
757 dcode->m_Drill.y =
758 KiROUND( ReadDouble( aText ) * conv_scale );
759
761 }
762 dcode->m_Defined = true;
763 break;
764
765 case 'O': // oval
766 case 'R': // rect
767 dcode->m_ApertType = (stdAperture == 'O') ? APT_OVAL : APT_RECT;
768
769 while( *aText == ' ' )
770 aText++;
771
772 if( *aText == 'X' ) // Second parameter: size Y
773 {
774 aText++;
775 dcode->m_Size.y =
776 KiROUND( ReadDouble( aText ) * conv_scale );
777 }
778
779 while( *aText == ' ' )
780 aText++;
781
782 if( *aText == 'X' ) // third parameter: drill size (or drill size X)
783 {
784 aText++;
785 dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
786 dcode->m_Drill.y = dcode->m_Drill.x;
788 }
789
790 while( *aText == ' ' )
791 aText++;
792
793 if( *aText == 'X' ) // fourth parameter: drill size Y
794 {
795 aText++;
796 dcode->m_Drill.y =
797 KiROUND( ReadDouble( aText ) * conv_scale );
799 }
800
801 dcode->m_Defined = true;
802 break;
803
804 case 'P':
805
806 /* Regular polygon: a command line like %ADD12P,0.040X10X25X0.025X0.025X0.0150*%
807 * params are: <diameter>, X<edge count>, X<Rotation>, X<X hole dim>, X<Y hole dim>
808 */
809 dcode->m_ApertType = APT_POLYGON;
810 while( *aText == ' ' )
811 aText++;
812
813 if( *aText == 'X' )
814 {
815 aText++;
816 dcode->m_EdgesCount = ReadInt( aText );
817 }
818
819 while( *aText == ' ' )
820 aText++;
821
822 if( *aText == 'X' )
823 {
824 aText++;
825 dcode->m_Rotation = EDA_ANGLE( ReadDouble( aText ), DEGREES_T );
826 }
827
828 while( *aText == ' ' )
829 aText++;
830
831 if( *aText == 'X' )
832 {
833 aText++;
834 dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
835 dcode->m_Drill.y = dcode->m_Drill.x;
837 }
838
839 while( *aText == ' ' )
840 aText++;
841
842 if( *aText == 'X' )
843 {
844 aText++;
845 dcode->m_Drill.y = KiROUND( ReadDouble( aText ) * conv_scale );
847 }
848 dcode->m_Defined = true;
849 break;
850 }
851 }
852 else // aText[0] starts an aperture macro name
853 {
854 APERTURE_MACRO am_lookup;
855
856 while( *aText && *aText != '*' && *aText != ',' )
857 am_lookup.m_AmName.Append( *aText++ );
858
859 // When an aperture definition is like %AMLINE2* 22,1,$1,$2,0,0,-45*
860 // the ADDxx<MACRO_NAME> command has parameters, like %ADD14LINE2,0.8X0.5*%
861 if( *aText == ',' )
862 { // Read aperture macro parameters and store them
863 aText++; // aText points the first parameter
864
865 while( *aText && *aText != '*' )
866 {
867 double param = ReadDouble( aText );
868 dcode->AppendParam( param );
869
870 if( !( isspace( *aText ) || *aText == 'X' || *aText == 'x' || *aText == '*' ) )
871 {
872 msg.Printf( wxT( "RS274X: aperture macro %s has invalid template "
873 "parameters\n" ),
874 TO_UTF8( am_lookup.m_AmName ) );
875 AddMessageToList( msg );
876 ok = false;
877 break;
878 }
879
880 while( isspace( *aText ) )
881 aText++;
882
883 // Skip 'X' separator:
884 if( *aText == 'X' || *aText == 'x' )
885 aText++;
886 }
887 }
888
889 // lookup the aperture macro here.
890 APERTURE_MACRO* pam = FindApertureMacro( am_lookup );
891
892 if( !pam )
893 {
894 msg.Printf( wxT( "RS274X: aperture macro %s not found\n" ),
895 TO_UTF8( am_lookup.m_AmName ) );
896 AddMessageToList( msg );
897 ok = false;
898 break;
899 }
900
901 dcode->m_ApertType = APT_MACRO;
902 dcode->SetMacro( pam );
903 dcode->m_Defined = true;
904 }
905
906 break;
907
908 default:
909 ok = false;
910 break;
911 }
912
913 ignore_unused( seq_len );
914
915 ok = GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
916
917 return ok;
918}
919
920
921bool GERBER_FILE_IMAGE::GetEndOfBlock( char* aBuff, unsigned int aBuffSize, char*& aText, FILE* gerber_file )
922{
923 for( ; ; )
924 {
925 while( (aText < aBuff + aBuffSize) && *aText )
926 {
927 if( *aText == '*' )
928 return true;
929
930 if( *aText == '%' )
931 return true;
932
933 aText++;
934 }
935
936 if( fgets( aBuff, aBuffSize, gerber_file ) == nullptr )
937 break;
938
939 m_LineNum++;
940 aText = aBuff;
941 }
942
943 return false;
944}
945
946
947char* GERBER_FILE_IMAGE::GetNextLine( char *aBuff, unsigned int aBuffSize, char* aText, FILE* aFile )
948{
949 for( ; ; )
950 {
951 switch (*aText )
952 {
953 case ' ': // skip blanks
954 case '\n':
955 case '\r': // Skip line terminators
956 ++aText;
957 break;
958
959 case 0: // End of text found in aBuff: Read a new string
960 if( fgets( aBuff, aBuffSize, aFile ) == nullptr )
961 return nullptr;
962
963 m_LineNum++;
964 aText = aBuff;
965 return aText;
966
967 default:
968 return aText;
969 }
970 }
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
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.
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:947
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:145
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:921
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:200
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:117
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 SCALE_FACTOR(x)
#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
RS274X_PARAMETERS
Definition: rs274x.cpp:58
@ FILE_ATTRIBUTE
Definition: rs274x.cpp:88
@ IMAGE_JUSTIFY
Definition: rs274x.cpp:72
@ APERTURE_ATTRIBUTE
Definition: rs274x.cpp:96
@ FORMAT_STATEMENT
Definition: rs274x.cpp:62
@ NET_ATTRIBUTE
Definition: rs274x.cpp:93
@ IMAGE_OFFSET
Definition: rs274x.cpp:74
@ ROTATE
Definition: rs274x.cpp:110
@ STEP_AND_REPEAT
Definition: rs274x.cpp:109
@ LOAD_NAME
Definition: rs274x.cpp:113
@ REMOVE_APERTURE_ATTRIBUTE
Definition: rs274x.cpp:102
@ KNOCKOUT
Definition: rs274x.cpp:108
@ AP_MACRO
Definition: rs274x.cpp:81
@ MIRROR_IMAGE
Definition: rs274x.cpp:63
@ MODE_OF_UNITS
Definition: rs274x.cpp:64
@ INCH
Definition: rs274x.cpp:65
@ IMAGE_NAME
Definition: rs274x.cpp:73
@ OFFSET
Definition: rs274x.cpp:67
@ MILLIMETER
Definition: rs274x.cpp:66
@ AXIS_SELECT
Definition: rs274x.cpp:61
@ SCALE_FACTOR
Definition: rs274x.cpp:68
@ IMAGE_ROTATION
Definition: rs274x.cpp:76
@ LOAD_POLARITY
Definition: rs274x.cpp:112
@ IMAGE_POLARITY
Definition: rs274x.cpp:75
@ AP_DEFINITION
Definition: rs274x.cpp:80
#define CODE(x, y)
Definition: rs274x.cpp:45
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
wxString From_UTF8(const char *cstring)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:378
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:588