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