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