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-2024 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
25
26#include <base_units.h>
27#include <math/util.h> // for KiROUND
28
29#include <gerbview.h>
30#include <gerber_file_image.h>
31#include <core/ignore.h>
32#include <macros.h>
33#include <string_utils.h>
35#include <gbr_metadata.h>
36#include <wx/log.h>
37
38extern int ReadInt( char*& text, bool aSkipSeparator = true );
39extern double ReadDouble( char*& text, bool aSkipSeparator = true );
40
41
42#define CODE( x, y ) ( ( (x) << 8 ) + (y) )
43
44// See rs274xrevd_e.pdf, table 1: RS-274X parameters order of entry
45// in gerber files, when a coordinate is given (like X78Y600 or I0J80):
46// Y and Y are logical coordinates
47// A and B are plotter coordinates
48// Usually A = X, B = Y
49// But we can have A = Y, B = X and/or offset, mirror, scale;
50// Also:
51// Image is what you must plot (the entire data of the file).
52// Layer is just a set of data blocks with their parameters. An image can have more than one
53// layer so a gerber layer is not like a board layer or the graphic layers used in GerbView
54// to show a file.
56 // Directive parameters: single usage recommended
57 // Must be at the beginning of the file
58 AXIS_SELECT = CODE( 'A', 'S' ), // Default: A=X, B=Y
59 FORMAT_STATEMENT = CODE( 'F', 'S' ), // no default: this command must exists
60 MIRROR_IMAGE = CODE( 'M', 'I' ), // Default: mo mirror
61 MODE_OF_UNITS = CODE( 'M', 'O' ), // Default: inch
62 INCH = CODE( 'I', 'N' ),
63 MILLIMETER = CODE( 'M', 'M' ),
64 OFFSET = CODE( 'O', 'F' ), // Default: A = 0, B = 0
65 SCALE_FACTOR = CODE( 'S', 'F' ), // Default: A = 1.0, B = 1.0
66
67 // Image parameters:
68 // commands used only once at the beginning of the file, and are deprecated
69 IMAGE_JUSTIFY = CODE( 'I', 'J' ), // Default: no justification
70 IMAGE_NAME = CODE( 'I', 'N' ), // Default: void
71 IMAGE_OFFSET = CODE( 'I', 'O' ), // Default: A = 0, B = 0
72 IMAGE_POLARITY = CODE( 'I', 'P' ), // Default: Positive
73 IMAGE_ROTATION = CODE( 'I', 'R' ), // Default: 0
74
75 // Aperture parameters:
76 // Usually for the whole file
77 AP_DEFINITION = CODE( 'A', 'D' ),
78 AP_MACRO = CODE( 'A', 'M' ),
79
80 // X2 extension attribute commands
81 // Mainly are found standard attributes and user attributes
82 // standard attributes commands are:
83 // TF (file attribute) TO (net attribute)
84 // TA (aperture attribute) and TD (delete aperture attribute)
85 FILE_ATTRIBUTE = CODE( 'T', 'F' ),
86
87 // X2 extension Net attribute info
88 // Net attribute options are:
89 // TO (net attribute data): TO.CN or TO.P TO.N or TO.C
90 NET_ATTRIBUTE = CODE( 'T', 'O' ),
91
92 // X2 extension Aperture attribute TA
93 APERTURE_ATTRIBUTE = CODE( 'T', 'A' ),
94
95 // TD (delete aperture/object attribute):
96 // Delete aperture attribute added by %TA or Oblect attribute added b %TO
97 // TD (delete all) or %TD<attr name> to delete <attr name>.
98 // eg: TD.P or TD.N or TD.C ...
100
101 // Layer specific parameters
102 // May be used singly or may be layer specific
103 // These parameters are at the beginning of the file or layer
104 // and reset some layer parameters (like interpolation)
105 KNOCKOUT = CODE( 'K', 'O' ), // Default: off
106 STEP_AND_REPEAT = CODE( 'S', 'R' ), // Default: A = 1, B = 1
107 ROTATE = CODE( 'R', 'O' ), // Default: 0
108
109 LOAD_POLARITY = CODE( 'L', 'P' ), //LPC or LPD. Default: Dark (LPD)
110 LOAD_NAME = CODE( 'L', 'N' ), // Deprecated: equivalent to G04
111};
112
113
115{
116 /* reads two bytes of data and assembles them into an int with the first
117 * byte in the sequence put into the most significant part of a 16 bit value
118 */
119 int result;
120 int currbyte;
121
122 if( text && *text )
123 {
124 currbyte = *text++;
125 result = ( currbyte & 0xFF ) << 8;
126 }
127 else
128 return -1;
129
130 if( text && *text )
131 {
132 currbyte = *text++;
133 result += currbyte & 0xFF;
134 }
135 else
136 return -1;
137
138 return result;
139}
140
141
142bool GERBER_FILE_IMAGE::ReadRS274XCommand( char *aBuff, unsigned int aBuffSize, char*& aText )
143{
144 bool ok = true;
145 int code_command;
146
147 aText++;
148
149 for( ; ; )
150 {
151 while( *aText )
152 {
153 switch( *aText )
154 {
155 case '%': // end of command
156 aText++;
158 goto exit; // success completion
159
160 case ' ':
161 case '\r':
162 case '\n':
163 aText++;
164 break;
165
166 case '*':
167 aText++;
168 break;
169
170 default:
171 code_command = ReadXCommandID( aText );
172 ok = ExecuteRS274XCommand( code_command, aBuff, aBuffSize, aText );
173
174 if( !ok )
175 goto exit;
176
177 break;
178 }
179 }
180
181 // end of current line, read another one.
182 if( fgets( aBuff, aBuffSize, m_Current_File ) == nullptr )
183 {
184 // end of file
185 ok = false;
186 break;
187 }
188 m_LineNum++;
189 aText = aBuff;
190 }
191
192exit:
193 return ok;
194}
195
196
197bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff,
198 unsigned int aBuffSize, char*& aText )
199{
200 int code;
201 int seq_len; // not used, just provided
202 int seq_char;
203 bool ok = true;
204 wxString msg;
205 double fcoord;
206 bool x_fmt_known = false;
207 bool y_fmt_known = false;
208
209 // conv_scale = scaling factor from inch to Internal Unit
210 double conv_scale = gerbIUScale.IU_PER_MILS * 1000;
211
212 if( m_GerbMetric )
213 conv_scale /= 25.4;
214
215 switch( aCommand )
216 {
217 case FORMAT_STATEMENT:
218 seq_len = 2;
219
220 while( *aText != '*' )
221 {
222 switch( *aText )
223 {
224 case ' ':
225 aText++;
226 break;
227
228 case 'D': // Non-standard option for all zeros (leading + tailing)
229 msg.Printf( _( "RS274X: Invalid GERBER format command '%c' at line %d: \"%s\"" ),
230 'D', m_LineNum, aBuff );
231 AddMessageToList( msg );
232 msg.Printf( _("GERBER file \"%s\" may not display as intended." ),
233 m_FileName.ToAscii() );
234 AddMessageToList( msg );
236
237 case 'L': // No Leading 0
238 m_NoTrailingZeros = false;
239 aText++;
240 break;
241
242 case 'T': // No trailing 0
243 m_NoTrailingZeros = true;
244 aText++;
245 break;
246
247 case 'A': // Absolute coord
248 m_Relative = false;
249 aText++;
250 break;
251
252 case 'I': // Relative coord
253 m_Relative = true;
254 aText++;
255 break;
256
257 case 'G':
258 case 'N': // Sequence code (followed by one digit: the sequence len)
259 // (sometimes found before the X,Y sequence)
260 // Obscure option
261 aText++;
262 seq_char = *aText++;
263
264 if( (seq_char >= '0') && (seq_char <= '9') )
265 seq_len = seq_char - '0';
266
267 break;
268
269 case 'M': // Sequence code (followed by one digit: the sequence len)
270 // (sometimes found after the X,Y sequence)
271 // Obscure option
272 aText++;
273 code = *aText;
274
275 if( ( code >= '0' ) && ( code <= '9' ) )
276 aText++; // skip the digit
277
278 break;
279
280 case 'X':
281 case 'Y':
282 {
283 code = *(aText++);
284 char ctmp = *(aText++) - '0';
285
286 if( code == 'X' )
287 {
288 x_fmt_known = true;
289 // number of digits after the decimal point (0 to 7 allowed)
290 m_FmtScale.x = *aText - '0';
291 m_FmtLen.x = ctmp + m_FmtScale.x;
292
293 // m_FmtScale is 0 to 7
294 // (Old Gerber specification was 0 to 6)
295 if( m_FmtScale.x < 0 )
296 m_FmtScale.x = 0;
297
298 if( m_FmtScale.x > 7 )
299 m_FmtScale.x = 7;
300 }
301 else
302 {
303 y_fmt_known = true;
304 m_FmtScale.y = *aText - '0';
305 m_FmtLen.y = ctmp + m_FmtScale.y;
306
307 if( m_FmtScale.y < 0 )
308 m_FmtScale.y = 0;
309
310 if( m_FmtScale.y > 7 )
311 m_FmtScale.y = 7;
312 }
313
314 aText++;
315 }
316 break;
317
318 case '*':
319 break;
320
321 default:
322 msg.Printf( wxT( "Unknown id (%c) in FS command" ),
323 *aText );
324 AddMessageToList( msg );
325 GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
326 ok = false;
327 break;
328 }
329 }
330
331 if( !x_fmt_known || !y_fmt_known )
332 AddMessageToList( wxT( "RS274X: Format Statement (FS) without X or Y format" ) );
333
334 break;
335
336 case AXIS_SELECT: // command ASAXBY*% or %ASAYBX*%
337 m_SwapAxis = false;
338
339 if( strncasecmp( aText, "AYBX", 4 ) == 0 )
340 m_SwapAxis = true;
341
342 break;
343
344 case MIRROR_IMAGE: // command %MIA0B0*%, %MIA0B1*%, %MIA1B0*%, %MIA1B1*%
345 m_MirrorA = m_MirrorB = false;
346
347 while( *aText && *aText != '*' )
348 {
349 switch( *aText )
350 {
351 case 'A': // Mirror A axis ?
352 aText++;
353
354 if( *aText == '1' )
355 m_MirrorA = true;
356
357 break;
358
359 case 'B': // Mirror B axis ?
360 aText++;
361
362 if( *aText == '1' )
363 m_MirrorB = true;
364
365 break;
366
367 default:
368 aText++;
369 break;
370 }
371 }
372 break;
373
374 case MODE_OF_UNITS:
375 code = ReadXCommandID( aText );
376
377 if( code == INCH )
378 m_GerbMetric = false;
379 else if( code == MILLIMETER )
380 m_GerbMetric = true;
381
383 break;
384
385 case FILE_ATTRIBUTE: // Command %TF ...
386 {
388 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
389
390 if( dummy.IsFileFunction() )
391 {
392 delete m_FileFunction;
394
395 // Don't set this until we get a file function; other code expects m_IsX2_file == true
396 // to mean that we have a valid m_FileFunction
397 m_IsX2_file = true;
398 }
399 else if( dummy.IsFileMD5() )
400 {
401 m_MD5_value = dummy.GetPrm( 1 );
402 }
403 else if( dummy.IsFilePart() )
404 {
405 m_PartString = dummy.GetPrm( 1 );
406 }
407 }
408 break;
409
410 case APERTURE_ATTRIBUTE: // Command %TA
411 {
413 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
414
415 if( dummy.GetAttribute() == wxT( ".AperFunction" ) )
416 {
417 m_AperFunction = dummy.GetPrm( 1 );
418
419 // A few function values can have other parameters. Add them
420 for( int ii = 2; ii < dummy.GetPrmCount(); ii++ )
421 m_AperFunction << wxT( "," ) << dummy.GetPrm( ii );
422 }
423 }
424 break;
425
426 case NET_ATTRIBUTE: // Command %TO currently %TO.P %TO.N and %TO.C
427 {
429
430 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
431
432 if( dummy.GetAttribute() == wxT( ".N" ) )
433 {
436 }
437 else if( dummy.GetAttribute() == wxT( ".C" ) )
438 {
441 }
442 else if( dummy.GetAttribute() == wxT( ".P" ) )
443 {
447
448 if( dummy.GetPrmCount() > 3 )
449 {
451 FormatStringFromGerber( dummy.GetPrm( 3 ) ), true, true );
452 }
453 else
454 {
456 }
457 }
458 }
459 break;
460
461 case REMOVE_APERTURE_ATTRIBUTE: // Command %TD ...
462 {
464 dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
466 }
467 break;
468
469 case OFFSET: // command: OFAnnBnn (nn = float number) = layer Offset
470 m_Offset.x = m_Offset.y = 0;
471 while( *aText != '*' )
472 {
473 switch( *aText )
474 {
475 case 'A': // A axis offset in current unit (inch or mm)
476 aText++;
477 fcoord = ReadDouble( aText );
478 m_Offset.x = KiROUND( fcoord * conv_scale );
479 break;
480
481 case 'B': // B axis offset in current unit (inch or mm)
482 aText++;
483 fcoord = ReadDouble( aText );
484 m_Offset.y = KiROUND( fcoord * conv_scale );
485 break;
486 }
487 }
488 break;
489
490 case SCALE_FACTOR:
491 m_Scale.x = m_Scale.y = 1.0;
492 while( *aText != '*' )
493 {
494 switch( *aText )
495 {
496 case 'A': // A axis scale
497 aText++;
498 m_Scale.x = ReadDouble( aText );
499 break;
500
501 case 'B': // B axis scale
502 aText++;
503 m_Scale.y = ReadDouble( aText );
504 break;
505 }
506 }
507 break;
508
509 case IMAGE_OFFSET: // command: IOAnnBnn (nn = float number) = Image Offset
511 while( *aText != '*' )
512 {
513 switch( *aText )
514 {
515 case 'A': // A axis offset in current unit (inch or mm)
516 aText++;
517 fcoord = ReadDouble( aText );
518 m_ImageOffset.x = KiROUND( fcoord * conv_scale );
519 break;
520
521 case 'B': // B axis offset in current unit (inch or mm)
522 aText++;
523 fcoord = ReadDouble( aText );
524 m_ImageOffset.y = KiROUND( fcoord * conv_scale );
525 break;
526 }
527 }
528 break;
529
530 case IMAGE_ROTATION: // command IR0* or IR90* or IR180* or IR270*
531 if( strncasecmp( aText, "0*", 2 ) == 0 )
532 m_ImageRotation = 0;
533 else if( strncasecmp( aText, "90*", 3 ) == 0 )
534 m_ImageRotation = 90;
535 else if( strncasecmp( aText, "180*", 4 ) == 0 )
536 m_ImageRotation = 180;
537 else if( strncasecmp( aText, "270*", 4 ) == 0 )
538 m_ImageRotation = 270;
539 else
540 AddMessageToList( _( "RS274X: Command \"IR\" rotation value not allowed" ) );
541
542 break;
543
544 case STEP_AND_REPEAT: // command SR, like %SRX3Y2I5.0J2*%
545 m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
547 GetLayerParams().m_StepForRepeat.x = 0.0; // offset for Step and Repeat command
549 GetLayerParams().m_YRepeatCount = 1; // The repeat count
551
552 while( *aText && *aText != '*' )
553 {
554 switch( *aText )
555 {
556 case 'I': // X axis offset
557 aText++;
559 break;
560
561 case 'J': // Y axis offset
562 aText++;
564 break;
565
566 case 'X': // X axis repeat count
567 aText++;
569 break;
570
571 case 'Y': // Y axis offset
572 aText++;
574 break;
575
576 default:
577 aText++;
578 break;
579 }
580 }
581 break;
582
583 case IMAGE_JUSTIFY: // Command IJAnBn*
584 m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false)
585 m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false)
586 m_ImageJustifyOffset = VECTOR2I( 0, 0 ); // Image Justify Offset on XY axis (default = 0,0)
587 while( *aText && *aText != '*' )
588 {
589 // IJ command is (for A or B axis) AC or AL or A<coordinate>
590 switch( *aText )
591 {
592 case 'A': // A axis justify
593 aText++;
594
595 if( *aText == 'C' )
596 {
598 aText++;
599 }
600 else if( *aText == 'L' )
601 {
603 aText++;
604 }
605 else
606 {
607 m_ImageJustifyOffset.x = KiROUND( ReadDouble( aText ) * conv_scale);
608 }
609
610 break;
611
612 case 'B': // B axis justify
613 aText++;
614
615 if( *aText == 'C' )
616 {
618 aText++;
619 }
620 else if( *aText == 'L' )
621 {
623 aText++;
624 }
625 else
626 {
627 m_ImageJustifyOffset.y = KiROUND( ReadDouble( aText ) * conv_scale);
628 }
629
630 break;
631
632 default:
633 aText++;
634 break;
635 }
636 }
637
640
643
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 ROTATE: // Layer rotation: command like %RO45*%
653 m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
654 m_LocalRotation = ReadDouble( aText ); // Store layer rotation in degrees
655 break;
656
657 case IMAGE_NAME:
658 m_ImageName.Empty();
659
660 while( *aText != '*' )
661 m_ImageName.Append( *aText++ );
662
663 break;
664
665 case LOAD_NAME:
666 // %LN is a (deprecated) equivalentto G04: a comment
667 while( *aText && *aText != '*' )
668 aText++; // Skip text
669
670 break;
671
672 case IMAGE_POLARITY:
673 // These commands are deprecated since 2012.
674 // So do nothing and prompt the user about this command
675 if( strncasecmp( aText, "NEG", 3 ) == 0 )
676 {
677 m_ImageNegative = true;
678 // IPPOS Gerber command is deprecated since 2012.
679 // in Gerber doc 2024, the advice is: warn user and skip it.
680 AddMessageToList( _( "IPNEG Gerber command is deprecated since 2012. Skip it" ) );
681 }
682 else
683 {
684 m_ImageNegative = false;
685 // IPPOS Gerber command is deprecated since 2012.
686 // However this is the default for a Gerber file, and does not have
687 // actual effect. Just skip it.
688 }
689
690 ok = false;
691 break;
692
693 case LOAD_POLARITY:
694 if( *aText == 'C' )
696 else
698
699 break;
700
701 case AP_MACRO: // lines like %AMMYMACRO*
702 // 5,1,8,0,0,1.08239X$1,22.5*
703 // %
704 /*ok = */ReadApertureMacro( aBuff, aBuffSize, aText, m_Current_File );
705 break;
706
707 case AP_DEFINITION:
708 /* input example: %ADD30R,0.081800X0.101500*%
709 * Aperture definition has 4 options: C, R, O, P
710 * (Circle, Rect, Oval, regular Polygon)
711 * and shapes can have a hole (round or rectangular).
712 * All optional parameters values start by X
713 * at this point, text points to 2nd 'D'
714 */
715 if( *aText++ != 'D' )
716 {
717 ok = false;
718 break;
719 }
720
721 m_Has_DCode = true;
722
723 code = ReadInt( aText );
724
725 D_CODE* dcode;
726 dcode = GetDCODEOrCreate( code );
727
728 if( dcode == nullptr )
729 break;
730
732
733 // at this point, text points to character after the ADD<num>,
734 // i.e. R in example above. If aText[0] is one of the usual
735 // apertures: (C,R,O,P), there is a comma after it.
736 if( aText[1] == ',' )
737 {
738 char stdAperture = *aText;
739
740 aText += 2; // skip "C," for example
741
742 // First parameter is the size X:
743 dcode->m_Size.x = KiROUND( ReadDouble( aText ) * conv_scale );
744 dcode->m_Size.y = dcode->m_Size.x;
745
746 switch( stdAperture ) // Aperture desceiption has optional parameters. Read them
747 {
748 case 'C': // Circle
749 dcode->m_ApertType = APT_CIRCLE;
750 while( *aText == ' ' )
751 aText++;
752
753 if( *aText == 'X' )
754 {
755 aText++;
756 dcode->m_Drill.x = dcode->m_Drill.y =
757 KiROUND( ReadDouble( aText ) * conv_scale );
759 }
760
761 while( *aText == ' ' )
762 aText++;
763
764 if( *aText == 'X' )
765 {
766 aText++;
767 dcode->m_Drill.y =
768 KiROUND( ReadDouble( aText ) * conv_scale );
769
771 }
772 dcode->m_Defined = true;
773 break;
774
775 case 'O': // oval
776 case 'R': // rect
777 dcode->m_ApertType = (stdAperture == 'O') ? APT_OVAL : APT_RECT;
778
779 while( *aText == ' ' )
780 aText++;
781
782 if( *aText == 'X' ) // Second parameter: size Y
783 {
784 aText++;
785 dcode->m_Size.y =
786 KiROUND( ReadDouble( aText ) * conv_scale );
787 }
788
789 while( *aText == ' ' )
790 aText++;
791
792 if( *aText == 'X' ) // third parameter: drill size (or drill size X)
793 {
794 aText++;
795 dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
796 dcode->m_Drill.y = dcode->m_Drill.x;
798 }
799
800 while( *aText == ' ' )
801 aText++;
802
803 if( *aText == 'X' ) // fourth parameter: drill size Y
804 {
805 aText++;
806 dcode->m_Drill.y =
807 KiROUND( ReadDouble( aText ) * conv_scale );
809 }
810
811 dcode->m_Defined = true;
812 break;
813
814 case 'P':
815
816 /* Regular polygon: a command line like %ADD12P,0.040X10X25X0.025X0.025X0.0150*%
817 * params are: <diameter>, X<edge count>, X<Rotation>, X<X hole dim>, X<Y hole dim>
818 */
819 dcode->m_ApertType = APT_POLYGON;
820 while( *aText == ' ' )
821 aText++;
822
823 if( *aText == 'X' )
824 {
825 aText++;
826 dcode->m_EdgesCount = ReadInt( aText );
827 }
828
829 while( *aText == ' ' )
830 aText++;
831
832 if( *aText == 'X' )
833 {
834 aText++;
835 dcode->m_Rotation = EDA_ANGLE( ReadDouble( aText ), DEGREES_T );
836 }
837
838 while( *aText == ' ' )
839 aText++;
840
841 if( *aText == 'X' )
842 {
843 aText++;
844 dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
845 dcode->m_Drill.y = dcode->m_Drill.x;
847 }
848
849 while( *aText == ' ' )
850 aText++;
851
852 if( *aText == 'X' )
853 {
854 aText++;
855 dcode->m_Drill.y = KiROUND( ReadDouble( aText ) * conv_scale );
857 }
858 dcode->m_Defined = true;
859 break;
860 }
861 }
862 else // aText[0] starts an aperture macro name
863 {
864 APERTURE_MACRO am_lookup;
865
866 while( *aText && *aText != '*' && *aText != ',' )
867 am_lookup.m_AmName.Append( *aText++ );
868
869 // When an aperture definition is like %AMLINE2* 22,1,$1,$2,0,0,-45*
870 // the ADDxx<MACRO_NAME> command has parameters, like %ADD14LINE2,0.8X0.5*%
871 if( *aText == ',' )
872 { // Read aperture macro parameters and store them
873 aText++; // aText points the first parameter
874
875 while( *aText && *aText != '*' )
876 {
877 double param = ReadDouble( aText );
878 dcode->AppendParam( param );
879
880 if( !( isspace( *aText ) || *aText == 'X' || *aText == 'x' || *aText == '*' ) )
881 {
882 msg.Printf( wxT( "RS274X: aperture macro %s has invalid template "
883 "parameters\n" ),
884 TO_UTF8( am_lookup.m_AmName ) );
885 AddMessageToList( msg );
886 ok = false;
887 break;
888 }
889
890 while( isspace( *aText ) )
891 aText++;
892
893 // Skip 'X' separator:
894 if( *aText == 'X' || *aText == 'x' )
895 aText++;
896 }
897 }
898
899 // lookup the aperture macro here.
900 APERTURE_MACRO* pam = FindApertureMacro( am_lookup );
901
902 if( !pam )
903 {
904 msg.Printf( wxT( "RS274X: aperture macro %s not found\n" ),
905 TO_UTF8( am_lookup.m_AmName ) );
906 AddMessageToList( msg );
907 ok = false;
908 break;
909 }
910
911 dcode->m_ApertType = APT_MACRO;
912 dcode->SetMacro( pam );
913 dcode->m_Defined = true;
914 }
915
916 break;
917
918 default:
919 ok = false;
920 break;
921 }
922
923 ignore_unused( seq_len );
924
925 ok = GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
926
927 return ok;
928}
929
930
931bool GERBER_FILE_IMAGE::GetEndOfBlock( char* aBuff, unsigned int aBuffSize, char*& aText, FILE* gerber_file )
932{
933 for( ; ; )
934 {
935 while( (aText < aBuff + aBuffSize) && *aText )
936 {
937 if( *aText == '*' )
938 return true;
939
940 if( *aText == '%' )
941 return true;
942
943 aText++;
944 }
945
946 if( fgets( aBuff, aBuffSize, gerber_file ) == nullptr )
947 break;
948
949 m_LineNum++;
950 aText = aBuff;
951 }
952
953 return false;
954}
955
956
957char* GERBER_FILE_IMAGE::GetNextLine( char *aBuff, unsigned int aBuffSize, char* aText, FILE* aFile )
958{
959 for( ; ; )
960 {
961 switch (*aText )
962 {
963 case ' ': // skip blanks
964 case '\n':
965 case '\r': // Skip line terminators
966 ++aText;
967 break;
968
969 case 0: // End of text found in aBuff: Read a new string
970 if( fgets( aBuff, aBuffSize, aFile ) == nullptr )
971 return nullptr;
972
973 m_LineNum++;
974 aText = aBuff;
975 return aText;
976
977 default:
978 return aText;
979 }
980 }
981}
982
983
984bool GERBER_FILE_IMAGE::ReadApertureMacro( char *aBuff, unsigned int aBuffSize,
985 char*& aText,
986 FILE* gerber_file )
987{
988 wxString msg;
990
991 // read macro name
992 while( *aText )
993 {
994 if( *aText == '*' )
995 {
996 ++aText;
997 break;
998 }
999
1000 am.m_AmName.Append( *aText++ );
1001 }
1002
1003 // Read aperture macro parameters
1004 for( ; ; )
1005 {
1006 if( *aText == '*' )
1007 ++aText;
1008
1009 aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1010
1011 if( aText == nullptr ) // End of File
1012 return false;
1013
1014 // aText points the beginning of a new line.
1015
1016 // Test for the last line in aperture macro lis:
1017 // last line is % or *% sometime found.
1018 if( *aText == '*' )
1019 ++aText;
1020
1021 if( *aText == '%' )
1022 break; // exit with aText still pointing at %
1023
1024 int paramCount = 0; // will be set to the minimal parameters count,
1025 // depending on the actual primitive
1026 int primitive_type = AMP_UNKNOWN;
1027 // Test for a valid symbol at the beginning of a description:
1028 // it can be: a parameter declaration like $1=$2/4
1029 // or a digit (macro primitive selection)
1030 // all other symbols are illegal.
1031 if( *aText == '$' ) // local parameter declaration, inside the aperture macro
1032 {
1035 aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1036
1037 if( aText == nullptr) // End of File
1038 return false;
1039
1040 param.ReadParamFromAmDef( aText );
1041 continue;
1042 }
1043 else if( !isdigit(*aText) ) // Ill. symbol
1044 {
1045 msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": ill. symbol, line: \"%s\"" ),
1046 am.m_AmName, From_UTF8( aBuff ) );
1047 AddMessageToList( msg );
1048 primitive_type = AMP_COMMENT;
1049 }
1050 else
1051 primitive_type = ReadInt( aText );
1052
1053 bool is_comment = false;
1054
1055 switch( primitive_type )
1056 {
1057 case AMP_COMMENT: // lines starting by 0 are a comment
1058 paramCount = 0;
1059 is_comment = true;
1060
1061 // Skip comment
1063
1064 break;
1065
1066 case AMP_CIRCLE:
1067 paramCount = 4; // minimal count. can have a optional parameter (rotation)
1068 break;
1069
1070 case AMP_LINE2:
1071 case AMP_LINE20:
1072 paramCount = 7;
1073 break;
1074
1075 case AMP_LINE_CENTER:
1077 paramCount = 6;
1078 break;
1079
1080 case AMP_OUTLINE:
1081 paramCount = 4; // partial count. other parameters are vertices and rotation
1082 // Second parameter is vertice (coordinate pairs) count.
1083 break;
1084
1085 case AMP_POLYGON:
1086 paramCount = 6;
1087 break;
1088
1089 case AMP_MOIRE:
1090 paramCount = 9;
1091 break;
1092
1093 case AMP_THERMAL:
1094 paramCount = 6;
1095 break;
1096
1097 default:
1098 msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Invalid primitive id code %d, line %d: \"%s\"" ),
1099 am.m_AmName, primitive_type, m_LineNum, From_UTF8( aBuff ) );
1100 AddMessageToList( msg );
1101 return false;
1102 }
1103
1104 if( is_comment )
1105 continue;
1106
1107 AM_PRIMITIVE prim( m_GerbMetric );
1108 prim.m_Primitive_id = (AM_PRIMITIVE_ID) primitive_type;
1109 int ii;
1110
1111 for( ii = 0; ii < paramCount && *aText && *aText != '*'; ++ii )
1112 {
1113 prim.m_Params.push_back( AM_PARAM() );
1114
1115 AM_PARAM& param = prim.m_Params.back();
1116
1117 aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1118
1119 if( aText == nullptr) // End of File
1120 return false;
1121
1122 param.ReadParamFromAmDef( aText );
1123 }
1124
1125 if( ii < paramCount )
1126 {
1127 // maybe some day we can throw an exception and track a line number
1128 msg.Printf( wxT( "RS274X: read macro descr type %d: read %d parameters, insufficient "
1129 "parameters\n" ),
1130 prim.m_Primitive_id, ii );
1131 AddMessageToList( msg );
1132 }
1133
1134 // there are more parameters to read if this is an AMP_OUTLINE
1135 if( prim.m_Primitive_id == AMP_OUTLINE )
1136 {
1137 // so far we have read [0]:exposure, [1]:#points, [2]:X start, [3]: Y start
1138 // Now read all the points, plus trailing rotation in degrees.
1139
1140 // m_Params[1] is a count of polygon points, so it must be given
1141 // in advance, i.e. be immediate.
1142 wxASSERT( prim.m_Params[1].IsImmediate() );
1143
1144 paramCount = (int) prim.m_Params[1].GetValueFromMacro( nullptr ) * 2 + 1;
1145
1146 for( int jj = 0; jj < paramCount && *aText != '*'; ++jj )
1147 {
1148 prim.m_Params.push_back( AM_PARAM() );
1149
1150 AM_PARAM& param = prim.m_Params.back();
1151
1152 aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
1153
1154 if( aText == nullptr ) // End of File
1155 return false;
1156
1157 param.ReadParamFromAmDef( aText );
1158 }
1159 }
1160
1161 // AMP_CIRCLE can have a optional parameter (rotation)
1162 if( prim.m_Primitive_id == AMP_CIRCLE && aText && *aText != '*' )
1163 {
1164 prim.m_Params.push_back( AM_PARAM() );
1165 AM_PARAM& param = prim.m_Params.back();
1166 param.ReadParamFromAmDef( aText );
1167 }
1168
1169 // The primitive description is now parsed: push it to the current aperture macro
1170 am.AddPrimitiveToList( prim );
1171 }
1172
1173 m_aperture_macros.insert( am );
1174
1175 return true;
1176}
1177
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:107
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
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.
static char m_LineBuffer[GERBER_BUFZ+1]
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:957
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:142
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:931
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:984
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:197
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:114
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.
#define GERBER_BUFZ
@ 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:55
@ FILE_ATTRIBUTE
Definition: rs274x.cpp:85
@ IMAGE_JUSTIFY
Definition: rs274x.cpp:69
@ APERTURE_ATTRIBUTE
Definition: rs274x.cpp:93
@ FORMAT_STATEMENT
Definition: rs274x.cpp:59
@ NET_ATTRIBUTE
Definition: rs274x.cpp:90
@ IMAGE_OFFSET
Definition: rs274x.cpp:71
@ ROTATE
Definition: rs274x.cpp:107
@ STEP_AND_REPEAT
Definition: rs274x.cpp:106
@ LOAD_NAME
Definition: rs274x.cpp:110
@ REMOVE_APERTURE_ATTRIBUTE
Definition: rs274x.cpp:99
@ KNOCKOUT
Definition: rs274x.cpp:105
@ AP_MACRO
Definition: rs274x.cpp:78
@ MIRROR_IMAGE
Definition: rs274x.cpp:60
@ MODE_OF_UNITS
Definition: rs274x.cpp:61
@ INCH
Definition: rs274x.cpp:62
@ IMAGE_NAME
Definition: rs274x.cpp:70
@ OFFSET
Definition: rs274x.cpp:64
@ MILLIMETER
Definition: rs274x.cpp:63
@ AXIS_SELECT
Definition: rs274x.cpp:58
@ SCALE_FACTOR
Definition: rs274x.cpp:65
@ IMAGE_ROTATION
Definition: rs274x.cpp:73
@ LOAD_POLARITY
Definition: rs274x.cpp:109
@ IMAGE_POLARITY
Definition: rs274x.cpp:72
@ AP_DEFINITION
Definition: rs274x.cpp:77
#define CODE(x, y)
Definition: rs274x.cpp:42
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:398
const double IU_PER_MILS
Definition: base_units.h:77
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691