1 /*
2  * gEDA - GNU Electronic Design Automation
3  * This is a part of gerbv
4  *
5  *   Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
6  *
7  * $Id$
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
22  */
23 
24 /** \file gerber.c
25     \brief RS274X parsing functions
26     \ingroup libgerbv
27 */
28 
29 #include "gerbv.h"
30 
31 #include <stdlib.h>
32 #include <string.h>
33 #include <math.h>  /* pow() */
34 #include <errno.h>
35 #include <ctype.h>
36 
37 #include "common.h"
38 #include "gerb_image.h"
39 #include "gerber.h"
40 #include "gerb_stats.h"
41 #include "amacro.h"
42 
43 #undef AMACRO_DEBUG
44 #define dprintf if(DEBUG) printf
45 
46 #define A2I(a,b) (((a & 0xff) << 8) + (b & 0xff))
47 
48 #define MAXL 200
49 
50 /* Local function prototypes */
51 static void parse_G_code(gerb_file_t *fd, gerb_state_t *state,
52 			 gerbv_image_t *image, long int *line_num_p);
53 static void parse_D_code(gerb_file_t *fd, gerb_state_t *state,
54 			 gerbv_image_t *image, long int *line_num_p);
55 static int parse_M_code(gerb_file_t *fd, gerbv_image_t *image,
56 			long int *line_num_p);
57 static void parse_rs274x(gint levelOfRecursion, gerb_file_t *fd,
58 			 gerbv_image_t *image, gerb_state_t *state,
59 			 gerbv_net_t *curr_net, gerbv_stats_t *stats,
60 			 gchar *directoryPath, long int *line_num_p);
61 static int parse_aperture_definition(gerb_file_t *fd,
62 				     gerbv_aperture_t *aperture,
63 				     gerbv_image_t *image, gdouble scale,
64 				     long int *line_num_p);
65 static void calc_cirseg_sq(struct gerbv_net *net, int cw,
66 			   double delta_cp_x, double delta_cp_y);
67 static void calc_cirseg_mq(struct gerbv_net *net, int cw,
68 			   double delta_cp_x, double delta_cp_y);
69 static void calc_cirseg_bbox(const gerbv_cirseg_t *cirseg,
70 			double apert_size_x, double apert_size_y,
71 			gerbv_render_size_t *bbox);
72 
73 static void gerber_update_any_running_knockout_measurements(
74 							gerbv_image_t *image);
75 
76 static void gerber_calculate_final_justify_effects (gerbv_image_t *image);
77 
78 static gboolean add_trailing_zeros_if_omitted(int *coord, int omitted_num,
79 						gerbv_format_t *format);
80 
81 gboolean knockoutMeasure = FALSE;
82 gdouble knockoutLimitXmin, knockoutLimitYmin,
83 	knockoutLimitXmax, knockoutLimitYmax;
84 gerbv_layer_t *knockoutLayer = NULL;
85 cairo_matrix_t currentMatrix;
86 
87 /* --------------------------------------------------------- */
88 gerbv_net_t *
gerber_create_new_net(gerbv_net_t * currentNet,gerbv_layer_t * layer,gerbv_netstate_t * state)89 gerber_create_new_net (gerbv_net_t *currentNet, gerbv_layer_t *layer, gerbv_netstate_t *state){
90 	gerbv_net_t *newNet = g_new0 (gerbv_net_t, 1);
91 
92 	currentNet->next = newNet;
93 	if (layer)
94 		newNet->layer = layer;
95 	else
96 		newNet->layer = currentNet->layer;
97 	if (state)
98 		newNet->state = state;
99 	else
100 		newNet->state = currentNet->state;
101 	return newNet;
102 }
103 
104 /* --------------------------------------------------------- */
105 gboolean
gerber_create_new_aperture(gerbv_image_t * image,int * indexNumber,gerbv_aperture_type_t apertureType,gdouble parameter1,gdouble parameter2)106 gerber_create_new_aperture (gerbv_image_t *image, int *indexNumber,
107 		gerbv_aperture_type_t apertureType, gdouble parameter1, gdouble parameter2){
108 	int i;
109 
110 	/* search for an available aperture spot */
111 	for (i = 0; i <= APERTURE_MAX; i++) {
112 		if (image->aperture[i] == NULL) {
113 			image->aperture[i] = g_new0 (gerbv_aperture_t, 1);
114 			image->aperture[i]->type = apertureType;
115 			image->aperture[i]->parameter[0] = parameter1;
116 			image->aperture[i]->parameter[1] = parameter2;
117 			*indexNumber = i;
118 			return TRUE;
119 		}
120 	}
121 	return FALSE;
122 }
123 
124 /* --------------------------------------------------------- */
125 /*! This function reads the Gerber file char by char, looking
126  *  for various Gerber codes (e.g. G, D, etc).  Once it reads
127  *  a code, it then dispatches control to one or another
128  *  bits of code which parse the individual code.
129  *  It also updates the state struct, which holds info about
130  *  the current state of the hypothetical photoplotter
131  *  (i.e. updates whether the aperture is on or off, updates
132  *  any other parameters, like units, offsets, apertures, etc.)
133  */
134 gboolean
gerber_parse_file_segment(gint levelOfRecursion,gerbv_image_t * image,gerb_state_t * state,gerbv_net_t * curr_net,gerbv_stats_t * stats,gerb_file_t * fd,gchar * directoryPath)135 gerber_parse_file_segment (gint levelOfRecursion, gerbv_image_t *image,
136 			   gerb_state_t *state,	gerbv_net_t *curr_net,
137 			   gerbv_stats_t *stats, gerb_file_t *fd,
138 			   gchar *directoryPath)
139 {
140     int read, coord, len, polygonPoints=0;
141     double x_scale = 0.0, y_scale = 0.0;
142     double delta_cp_x = 0.0, delta_cp_y = 0.0;
143     double aperture_sizeX, aperture_sizeY;
144     double scale;
145     gboolean foundEOF = FALSE;
146     gerbv_render_size_t boundingBoxNew = {HUGE_VAL,-HUGE_VAL,HUGE_VAL,-HUGE_VAL},
147 			boundingBox = boundingBoxNew;
148     gerbv_error_list_t *error_list = stats->error_list;
149     long int line_num = 1;
150 
151     while ((read = gerb_fgetc(fd)) != EOF) {
152         /* figure out the scale, since we need to normalize
153 	   all dimensions to inches */
154         if (state->state->unit == GERBV_UNIT_MM)
155             scale = 25.4;
156         else
157             scale = 1.0;
158 	switch ((char)(read & 0xff)) {
159 	case 'G':
160 	    dprintf("... Found G code at line %ld\n", line_num);
161 	    parse_G_code(fd, state, image, &line_num);
162 	    break;
163 	case 'D':
164 	    dprintf("... Found D code at line %ld\n", line_num);
165 	    parse_D_code(fd, state, image, &line_num);
166 	    break;
167 	case 'M':
168 	    dprintf("... Found M code at line %ld\n", line_num);
169 
170 	    switch(parse_M_code(fd, image, &line_num)) {
171 	    case 1 :
172 	    case 2 :
173 	    case 3 :
174 		foundEOF = TRUE;
175 		break;
176 	    default:
177 		gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
178 			_("Unknown M code found at line %ld in file \"%s\""),
179 			line_num, fd->filename);
180 	    } /* switch(parse_M_code) */
181 	    break;
182 	case 'X':
183 	    stats->X++;
184 	    coord = gerb_fgetint(fd, &len);
185 	    if (image->format)
186 		    add_trailing_zeros_if_omitted(&coord,
187 			    image->format->x_int + image->format->x_dec - len,
188 			    image->format);
189 	    dprintf("... Found X code %d at line %ld\n", coord, line_num);
190 	    if (image->format
191 	    &&  image->format->coordinate==GERBV_COORDINATE_INCREMENTAL)
192 	        state->curr_x += coord;
193 	    else
194 	        state->curr_x = coord;
195 
196 	    state->changed = 1;
197 	    break;
198 
199 	case 'Y':
200 	    stats->Y++;
201 	    coord = gerb_fgetint(fd, &len);
202 	    if (image->format)
203 		    add_trailing_zeros_if_omitted(&coord,
204 			    image->format->y_int + image->format->y_dec - len,
205 			    image->format);
206 	    dprintf("... Found Y code %d at line %ld\n", coord, line_num);
207 	    if (image->format
208 	    &&  image->format->coordinate==GERBV_COORDINATE_INCREMENTAL)
209 	        state->curr_y += coord;
210 	    else
211 	        state->curr_y = coord;
212 
213 	    state->changed = 1;
214 	    break;
215 
216 	case 'I':
217 	    stats->I++;
218 	    coord = gerb_fgetint(fd, &len);
219 	    if (image->format)
220 		    add_trailing_zeros_if_omitted(&coord,
221 			    image->format->x_int + image->format->x_dec - len,
222 			    image->format);
223 	    dprintf("... Found I code %d at line %ld\n", coord, line_num);
224 	    state->delta_cp_x = coord;
225 	    state->changed = 1;
226 	    break;
227 
228 	case 'J':
229 	    stats->J++;
230 	    coord = gerb_fgetint(fd, &len);
231 	    if (image->format)
232 		    add_trailing_zeros_if_omitted(&coord,
233 			    image->format->y_int + image->format->y_dec - len,
234 			    image->format);
235 	    dprintf("... Found J code %d at line %ld\n", coord, line_num);
236 	    state->delta_cp_y = coord;
237 	    state->changed = 1;
238 	    break;
239 
240 	case '%':
241 	    dprintf("... Found %% code at line %ld\n", line_num);
242 	    while (1) {
243 	    	parse_rs274x(levelOfRecursion, fd, image, state, curr_net,
244 				stats, directoryPath, &line_num);
245 
246 	    	/* advance past any whitespace here */
247 		int c;
248 		while (1) {
249 		    c = gerb_fgetc(fd);
250 
251 		    switch (c) {
252 		    case '\0': case '\t': case ' ':
253 
254 			continue;
255 
256 		    case '\n':
257 			line_num++;
258 
259 			/* Get <CR> char, if any, from <LF><CR> pair */
260 			read = gerb_fgetc(fd);
261 			if (read != '\r' && read != EOF)
262 			    gerb_ungetc(fd);
263 
264 			continue;
265 
266 		    case '\r':
267 			line_num++;
268 
269 			/* Get <LF> char, if any, from <CR><LF> pair */
270 			read = gerb_fgetc(fd);
271 			if (read != '\n' && read != EOF)
272 			    gerb_ungetc(fd);
273 
274 			continue;
275 		    }
276 
277 		    break; /* break while(1) */
278 		};
279 
280 		if(c == EOF || c == '%')
281 		    break;
282 
283 		/* Loop again to catch multiple blocks on the same line
284 		 * (separated by * char) */
285 		gerb_ungetc(fd);
286 	    }
287 	    break;
288 	case '*':
289 	    dprintf("... Found * code at line %ld\n", line_num);
290 	    stats->star++;
291 	    if (state->changed == 0) break;
292 	    state->changed = 0;
293 
294 	    /* don't even bother saving the net if the aperture state is GERBV_APERTURE_STATE_OFF and we
295 	       aren't starting a polygon fill (where we need it to get to the start point) */
296 	    if ((state->aperture_state == GERBV_APERTURE_STATE_OFF)&&(!state->in_parea_fill)&&
297 	    		(state->interpolation != GERBV_INTERPOLATION_PAREA_START)) {
298 		/* save the coordinate so the next net can use it for a start point */
299 		state->prev_x = state->curr_x;
300 		state->prev_y = state->curr_y;
301 		break;
302 	    }
303 	    curr_net = gerber_create_new_net (curr_net, state->layer, state->state);
304 	    /*
305 	     * Scale to given coordinate format
306 	     * XXX only "omit leading zeros".
307 	     */
308 	    if (image && image->format ){
309 		x_scale = pow(10.0, (double)image->format->x_dec);
310 		y_scale = pow(10.0, (double)image->format->y_dec);
311 	    }
312 	    x_scale *= scale;
313 	    y_scale *= scale;
314 	    curr_net->start_x = (double)state->prev_x / x_scale;
315 	    curr_net->start_y = (double)state->prev_y / y_scale;
316 	    curr_net->stop_x = (double)state->curr_x / x_scale;
317 	    curr_net->stop_y = (double)state->curr_y / y_scale;
318 	    delta_cp_x = (double)state->delta_cp_x / x_scale;
319 	    delta_cp_y = (double)state->delta_cp_y / y_scale;
320 
321 	    switch (state->interpolation) {
322 	    case GERBV_INTERPOLATION_CW_CIRCULAR :
323 	    case GERBV_INTERPOLATION_CCW_CIRCULAR : {
324 		int cw = (state->interpolation == GERBV_INTERPOLATION_CW_CIRCULAR);
325 
326 		curr_net->cirseg = g_new0 (gerbv_cirseg_t, 1);
327 		if (state->mq_on) {
328 		    calc_cirseg_mq(curr_net, cw, delta_cp_x, delta_cp_y);
329 		} else {
330 		    calc_cirseg_sq(curr_net, cw, delta_cp_x, delta_cp_y);
331 
332 		    /*
333 		     * In single quadrant circular interpolation Ix and Jy
334 		     * incremental distance must be unsigned.
335 		     */
336 		    if (delta_cp_x < 0 || delta_cp_y < 0) {
337 			gerbv_stats_printf(error_list,
338 				GERBV_MESSAGE_ERROR, -1,
339 				_("Signed incremental distance IxJy "
340 				    "in single quadrant %s circular "
341 				    "interpolation %s at line %ld "
342 				    "in file \"%s\""),
343 				cw? _("CW"): _("CCW"), cw? "G02": "G03",
344 				line_num, fd->filename);
345 		    }
346 
347 		}
348 		break;
349 	    }
350 	    case GERBV_INTERPOLATION_PAREA_START :
351 		/*
352 		 * To be able to get back and fill in number of polygon corners
353 		 */
354 		state->parea_start_node = curr_net;
355 		state->in_parea_fill = 1;
356 		polygonPoints = 0;
357 		boundingBox = boundingBoxNew;
358 		break;
359 	    case GERBV_INTERPOLATION_PAREA_END :
360 		/* save the calculated bounding box to the master node */
361 		if (state->parea_start_node != NULL) {
362 		    state->parea_start_node->boundingBox = boundingBox;
363 		} else {
364 		    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
365 			    _("End of polygon without start "
366 				"at line %ld in file \"%s\""),
367 			    line_num, fd->filename);
368 		}
369 
370 		/* close out the polygon */
371 		state->parea_start_node = NULL;
372 		state->in_parea_fill = 0;
373 		polygonPoints = 0;
374 		break;
375 	    default :
376 		break;
377 	    }  /* switch(state->interpolation) */
378 
379 	    /*
380 	     * Count number of points in Polygon Area
381 	     */
382 	    if (state->in_parea_fill && state->parea_start_node) {
383 		/*
384 		 * "...all lines drawn with D01 are considered edges of the
385 		 * polygon. D02 closes and fills the polygon."
386 		 * p.49 rs274xrevd_e.pdf
387 		 * D02 -> state->aperture_state == GERBV_APERTURE_STATE_OFF
388 		 */
389 
390 		 /* UPDATE: only end the polygon during a D02 call if we've already
391 		    drawn a polygon edge (with D01) */
392 
393 		if (state->aperture_state == GERBV_APERTURE_STATE_OFF
394 		&&  state->interpolation  != GERBV_INTERPOLATION_PAREA_START
395 		&&  polygonPoints > 0) {
396 		    curr_net->interpolation = GERBV_INTERPOLATION_PAREA_END;
397 		    curr_net = gerber_create_new_net (curr_net, state->layer, state->state);
398 		    curr_net->interpolation = GERBV_INTERPOLATION_PAREA_START;
399 		    state->parea_start_node->boundingBox = boundingBox;
400 		    state->parea_start_node = curr_net;
401 		    polygonPoints = 0;
402 		    curr_net = gerber_create_new_net (curr_net, state->layer, state->state);
403 		    curr_net->start_x = (double)state->prev_x / x_scale;
404 		    curr_net->start_y = (double)state->prev_y / y_scale;
405 		    curr_net->stop_x = (double)state->curr_x / x_scale;
406 		    curr_net->stop_y = (double)state->curr_y / y_scale;
407 		    boundingBox = boundingBoxNew;
408 		}
409 		else if (state->interpolation != GERBV_INTERPOLATION_PAREA_START)
410 		    polygonPoints++;
411 
412 	    }  /* if (state->in_parea_fill && state->parea_start_node) */
413 
414 	    curr_net->interpolation = state->interpolation;
415 
416 	    /*
417 	     * Override circular interpolation if no center was given.
418 	     * This should be a safe hack, since a good file should always
419 	     * include I or J.  And even if the radius is zero, the endpoint
420 	     * should be the same as the start point, creating no line
421 	     */
422 	    if (((state->interpolation == GERBV_INTERPOLATION_CW_CIRCULAR) ||
423 		 (state->interpolation == GERBV_INTERPOLATION_CCW_CIRCULAR)) &&
424 		((state->delta_cp_x == 0.0) && (state->delta_cp_y == 0.0)))
425 		curr_net->interpolation = GERBV_INTERPOLATION_LINEARx1;
426 
427 	    /*
428 	     * If we detected the end of Polygon Area Fill we go back to
429 	     * the interpolation we had before that.
430 	     * Also if we detected any of the quadrant flags, since some
431 	     * gerbers don't reset the interpolation (EagleCad again).
432 	     */
433 	    if ((state->interpolation == GERBV_INTERPOLATION_PAREA_START
434 	    ||   state->interpolation == GERBV_INTERPOLATION_PAREA_END)
435 	    && state->prev_interpolation != GERBV_INTERPOLATION_PAREA_END) {
436 		state->interpolation = state->prev_interpolation;
437 	    }
438 
439 	    /*
440 	     * Save layer polarity and unit
441 	     */
442 	    curr_net->layer = state->layer;
443 
444 	    state->delta_cp_x = 0.0;
445 	    state->delta_cp_y = 0.0;
446 	    curr_net->aperture = state->curr_aperture;
447 	    curr_net->aperture_state = state->aperture_state;
448 
449 	    /*
450 	     * For next round we save the current position as
451 	     * the previous position
452 	     */
453 	    state->prev_x = state->curr_x;
454 	    state->prev_y = state->curr_y;
455 
456 	    /*
457 	     * If we have an aperture defined at the moment we find
458 	     * min and max of image with compensation for mm.
459 	     */
460 	    if ((curr_net->aperture == 0) && !state->in_parea_fill)
461 		break;
462 
463 	    /* only update the min/max values and aperture stats if we are drawing */
464 	    if ((curr_net->aperture_state != GERBV_APERTURE_STATE_OFF)&&
465 	    		(curr_net->interpolation != GERBV_INTERPOLATION_PAREA_START)){
466 		double repeat_off_X = 0.0, repeat_off_Y = 0.0;
467 
468 		/* Update stats with current aperture number if not in polygon */
469 		if (!state->in_parea_fill) {
470 			dprintf("     In %s(), adding 1 to D_list ...\n",
471 					__func__);
472 			int retcode = gerbv_stats_increment_D_list_count(
473 				stats->D_code_list, curr_net->aperture,
474 				1, error_list);
475 			if (retcode == -1) {
476 			    gerbv_stats_printf(error_list,
477 				    GERBV_MESSAGE_ERROR, -1,
478 				    _("Found undefined D code D%02d "
479 					"at line %ld in file \"%s\""),
480 				    curr_net->aperture, line_num, fd->filename);
481 			    stats->D_unknown++;
482 			}
483 		}
484 
485 		/*
486 		 * If step_and_repeat (%SR%) is used, check min_x,max_y etc for
487 		 * the ends of the step_and_repeat lattice. This goes wrong in
488 		 * the case of negative dist_X or dist_Y, in which case we
489 		 * should compare against the startpoints of the lines, not
490 		 * the stoppoints, but that seems an uncommon case (and the
491 		 * error isn't very big any way).
492 		 */
493 		repeat_off_X = (state->layer->stepAndRepeat.X - 1) *
494 		    state->layer->stepAndRepeat.dist_X;
495 		repeat_off_Y = (state->layer->stepAndRepeat.Y - 1) *
496 		    state->layer->stepAndRepeat.dist_Y;
497 
498 		cairo_matrix_init (&currentMatrix, 1, 0, 0, 1, 0, 0);
499 		/* offset image */
500 		cairo_matrix_translate (&currentMatrix, image->info->offsetA,
501 					image->info->offsetB);
502 		/* do image rotation */
503 		cairo_matrix_rotate (&currentMatrix, image->info->imageRotation);
504 		/* it's a new layer, so recalculate the new transformation
505 		 * matrix for it */
506 		/* do any rotations */
507 		cairo_matrix_rotate (&currentMatrix, state->layer->rotation);
508 
509 		/* calculate current layer and state transformation matrices */
510 		/* apply scale factor */
511 		cairo_matrix_scale (&currentMatrix, state->state->scaleA,
512 				    state->state->scaleB);
513 		/* apply offset */
514 		cairo_matrix_translate (&currentMatrix, state->state->offsetA,
515 					state->state->offsetB);
516 		/* apply mirror */
517 		switch (state->state->mirrorState) {
518 		case GERBV_MIRROR_STATE_FLIPA:
519 		    cairo_matrix_scale (&currentMatrix, -1, 1);
520 		    break;
521 		case GERBV_MIRROR_STATE_FLIPB:
522 		    cairo_matrix_scale (&currentMatrix, 1, -1);
523 		    break;
524 		case GERBV_MIRROR_STATE_FLIPAB:
525 		    cairo_matrix_scale (&currentMatrix, -1, -1);
526 		    break;
527 		default:
528 		    break;
529 		}
530 		/* finally, apply axis select */
531 		if (state->state->axisSelect == GERBV_AXIS_SELECT_SWAPAB) {
532 		    /* we do this by rotating 270 (counterclockwise, then
533 		     *  mirroring the Y axis
534 		     */
535 		    cairo_matrix_rotate (&currentMatrix, M_PI + M_PI_2);
536 		    cairo_matrix_scale (&currentMatrix, 1, -1);
537 		}
538 		/* if it's a macro, step through all the primitive components
539 		   and calculate the true bounding box */
540 		if ((image->aperture[curr_net->aperture] != NULL) &&
541 		    (image->aperture[curr_net->aperture]->type == GERBV_APTYPE_MACRO)) {
542 		    gerbv_simplified_amacro_t *ls = image->aperture[curr_net->aperture]->simplified;
543 
544 		    while (ls != NULL) {
545 			gdouble offsetx = 0, offsety = 0, widthx = 0, widthy = 0;
546 			gboolean calculatedAlready = FALSE;
547 
548 			if (ls->type == GERBV_APTYPE_MACRO_CIRCLE) {
549 			    offsetx=ls->parameter[CIRCLE_CENTER_X];
550 			    offsety=ls->parameter[CIRCLE_CENTER_Y];
551 			    widthx=widthy=ls->parameter[CIRCLE_DIAMETER];
552 			} else if (ls->type == GERBV_APTYPE_MACRO_OUTLINE) {
553 			    int pointCounter,numberOfPoints;
554 			    numberOfPoints = ls->parameter[OUTLINE_NUMBER_OF_POINTS] + 1;
555 
556 			    for (pointCounter = 0; pointCounter < numberOfPoints; pointCounter++) {
557 				gerber_update_min_and_max (&boundingBox,
558 							   curr_net->stop_x +
559 							   ls->parameter[OUTLINE_X_IDX_OF_POINT(pointCounter)],
560 							   curr_net->stop_y +
561 							   ls->parameter[OUTLINE_Y_IDX_OF_POINT(pointCounter)],
562 							   0,0,0,0);
563 			    }
564 			    calculatedAlready = TRUE;
565 			} else if (ls->type == GERBV_APTYPE_MACRO_POLYGON) {
566 			    offsetx = ls->parameter[POLYGON_CENTER_X];
567 			    offsety = ls->parameter[POLYGON_CENTER_Y];
568 			    widthx = widthy = ls->parameter[POLYGON_DIAMETER];
569 			} else if (ls->type == GERBV_APTYPE_MACRO_MOIRE) {
570 			    offsetx = ls->parameter[MOIRE_CENTER_X];
571 			    offsety = ls->parameter[MOIRE_CENTER_Y];
572 			    widthx = widthy = ls->parameter[MOIRE_OUTSIDE_DIAMETER];
573 			} else if (ls->type == GERBV_APTYPE_MACRO_THERMAL) {
574 			    offsetx = ls->parameter[THERMAL_CENTER_X];
575 			    offsety = ls->parameter[THERMAL_CENTER_Y];
576 			    widthx = widthy = ls->parameter[THERMAL_OUTSIDE_DIAMETER];
577 			} else if (ls->type == GERBV_APTYPE_MACRO_LINE20) {
578 			    widthx = widthy = ls->parameter[LINE20_LINE_WIDTH];
579 			    gerber_update_min_and_max (&boundingBox,
580 						       curr_net->stop_x +
581 						       ls->parameter[LINE20_START_X],
582 						       curr_net->stop_y +
583 						       ls->parameter[LINE20_START_Y],
584 						       widthx/2,widthx/2,widthy/2,widthy/2);
585 			    gerber_update_min_and_max (&boundingBox,
586 						       curr_net->stop_x +
587 						       ls->parameter[LINE20_END_X],
588 						       curr_net->stop_y +
589 						       ls->parameter[LINE20_END_Y],
590 						       widthx/2,widthx/2,widthy/2,widthy/2);
591 			    calculatedAlready = TRUE;
592 			} else if (ls->type == GERBV_APTYPE_MACRO_LINE21) {
593 			    gdouble largestDimension = hypot(ls->parameter[LINE21_WIDTH],
594 							     ls->parameter[LINE21_HEIGHT]);
595 			    offsetx = ls->parameter[LINE21_CENTER_X];
596 			    offsety = ls->parameter[LINE21_CENTER_Y];
597 			    widthx = widthy = largestDimension;
598 			} else if (ls->type == GERBV_APTYPE_MACRO_LINE22) {
599 			    gdouble largestDimension = hypot(ls->parameter[LINE22_WIDTH],
600 							     ls->parameter[LINE22_HEIGHT]);
601 
602 			    offsetx = ls->parameter[LINE22_LOWER_LEFT_X] +
603 	      			ls->parameter[LINE22_WIDTH]/2;
604 			    offsety = ls->parameter[LINE22_LOWER_LEFT_Y] +
605 	      			ls->parameter[LINE22_HEIGHT]/2;
606 			    widthx = widthy=largestDimension;
607 			}
608 
609 			if (!calculatedAlready) {
610 			    gerber_update_min_and_max (&boundingBox,
611 						       curr_net->stop_x + offsetx,
612 						       curr_net->stop_y + offsety,
613 						       widthx/2,widthx/2,widthy/2,widthy/2);
614 			}
615 	    		ls = ls->next;
616 		    }
617 		} else {
618 		    if (image->aperture[curr_net->aperture] != NULL) {
619 			aperture_sizeX = image->aperture[curr_net->aperture]->parameter[0];
620 			if ((image->aperture[curr_net->aperture]->type == GERBV_APTYPE_RECTANGLE) || (image->aperture[curr_net->aperture]->type == GERBV_APTYPE_OVAL)) {
621 				aperture_sizeY = image->aperture[curr_net->aperture]->parameter[1];
622 			}
623 			else
624 				aperture_sizeY = aperture_sizeX;
625 		    } else {
626 			/* this is usually for polygon fills, where the aperture width
627 			   is "zero" */
628 			aperture_sizeX = aperture_sizeY = 0;
629 		    }
630 
631 		    /* if it's an arc path, use a special calc */
632 
633 		    if ((curr_net->interpolation ==
634 					GERBV_INTERPOLATION_CW_CIRCULAR) ||
635 			(curr_net->interpolation ==
636 					GERBV_INTERPOLATION_CCW_CIRCULAR)) {
637 				calc_cirseg_bbox(curr_net->cirseg,
638 						aperture_sizeX, aperture_sizeY,
639 						&boundingBox);
640 		    } else {
641 			    /* check both the start and stop of the aperture points against
642 			       a running min/max counter */
643 			    /* Note: only check start coordinate if this isn't a flash,
644 			       since the start point may be bogus if it is a flash */
645 			    if (curr_net->aperture_state != GERBV_APERTURE_STATE_FLASH) {
646 				gerber_update_min_and_max (&boundingBox,
647 							   curr_net->start_x, curr_net->start_y,
648 							   aperture_sizeX/2,aperture_sizeX/2,
649 							   aperture_sizeY/2,aperture_sizeY/2);
650 			    }
651 			    gerber_update_min_and_max (&boundingBox,
652 						       curr_net->stop_x, curr_net->stop_y,
653 						       aperture_sizeX/2,aperture_sizeX/2,
654 						       aperture_sizeY/2,aperture_sizeY/2);
655 		    }
656 
657 		}
658 		/* update the info bounding box with this latest bounding box */
659 		/* don't change the bounding box if the polarity is clear */
660 		if (state->layer->polarity != GERBV_POLARITY_CLEAR){
661 			gerber_update_image_min_max(&boundingBox, repeat_off_X, repeat_off_Y, image);
662 		}
663 		/* optionally update the knockout measurement box */
664 		if (knockoutMeasure) {
665 			if (boundingBox.left < knockoutLimitXmin)
666 				knockoutLimitXmin = boundingBox.left;
667 			if (boundingBox.right+repeat_off_X > knockoutLimitXmax)
668 				knockoutLimitXmax = boundingBox.right+repeat_off_X;
669 			if (boundingBox.bottom < knockoutLimitYmin)
670 				knockoutLimitYmin = boundingBox.bottom;
671 			if (boundingBox.top+repeat_off_Y > knockoutLimitYmax)
672 				knockoutLimitYmax = boundingBox.top+repeat_off_Y;
673 		}
674 		/* if we're not in a polygon fill, then update the object bounding box */
675 		if (!state->in_parea_fill) {
676 			curr_net->boundingBox = boundingBox;
677 			boundingBox = boundingBoxNew;
678 		}
679 	    }
680 	    break;
681 
682 	case '\0': case '\t': case ' ':
683 	    break;
684 
685 	case '\n':
686 	    line_num++;
687 
688 	    /* Get <CR> char, if any, from <LF><CR> pair */
689 	    read = gerb_fgetc(fd);
690 	    if (read != '\r' && read != EOF)
691 		    gerb_ungetc(fd);
692 	    break;
693 
694 	case '\r':
695 	    line_num++;
696 
697 	    /* Get <LF> char, if any, from <CR><LF> pair */
698 	    read = gerb_fgetc(fd);
699 	    if (read != '\n' && read != EOF)
700 		    gerb_ungetc(fd);
701 	    break;
702 
703 	default:
704 	    stats->unknown++;
705 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
706 		    _("Found unknown character '%s' (0x%x) "
707 			"at line %ld in file \"%s\""),
708 		    gerbv_escape_char(read), read,
709 		    line_num, fd->filename);
710 	}  /* switch((char) (read & 0xff)) */
711     }
712     return foundEOF;
713 }
714 
715 
716 /* ------------------------------------------------------------------ */
717 /*! This is a wrapper which gets called from top level.  It
718  *  does some initialization and pre-processing, and
719  *  then calls gerber_parse_file_segment
720  *  which processes the actual file.  Then it does final
721  *  modifications to the image created.
722  */
723 gerbv_image_t *
parse_gerb(gerb_file_t * fd,gchar * directoryPath)724 parse_gerb(gerb_file_t *fd, gchar *directoryPath)
725 {
726     gerb_state_t *state = NULL;
727     gerbv_image_t *image = NULL;
728     gerbv_net_t *curr_net = NULL;
729     gerbv_stats_t *stats;
730     gboolean foundEOF = FALSE;
731 
732     /* added by t.motylewski@bfad.de
733      * many locales redefine "." as "," and so on,
734      * so sscanf and strtod has problems when
735      * reading files using %f format */
736     setlocale(LC_NUMERIC, "C" );
737 
738     /*
739      * Create new state.  This is used locally to keep track
740      * of the photoplotter's state as the Gerber is read in.
741      */
742     state = g_new0 (gerb_state_t, 1);
743 
744     /*
745      * Create new image.  This will be returned.
746      */
747     image = gerbv_create_image(image, "RS274-X (Gerber) File");
748     if (image == NULL)
749 	GERB_FATAL_ERROR("malloc image failed in %s()", __FUNCTION__);
750     curr_net = image->netlist;
751     image->layertype = GERBV_LAYERTYPE_RS274X;
752     image->gerbv_stats = gerbv_stats_new();
753     if (image->gerbv_stats == NULL)
754 	GERB_FATAL_ERROR("malloc gerbv_stats failed in %s()", __FUNCTION__);
755 
756     stats = image->gerbv_stats;
757 
758     /* set active layer and netstate to point to first default one created */
759     state->layer = image->layers;
760     state->state = image->states;
761     curr_net->layer = state->layer;
762     curr_net->state = state->state;
763 
764     /*
765      * Start parsing
766      */
767     dprintf("In %s(), starting to parse file...\n", __func__);
768     foundEOF = gerber_parse_file_segment (1, image, state, curr_net, stats,
769 					  fd, directoryPath);
770 
771     if (!foundEOF) {
772 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
773 		_("Missing Gerber EOF code in file \"%s\""), fd->filename);
774     }
775     g_free(state);
776 
777     dprintf("               ... done parsing Gerber file\n");
778     gerber_update_any_running_knockout_measurements (image);
779     gerber_calculate_final_justify_effects(image);
780 
781     return image;
782 } /* parse_gerb */
783 
784 
785 /* ------------------------------------------------------------------- */
786 /*! Checks for signs that this is a RS-274X file
787  *  Returns TRUE if it is, FALSE if not.
788  */
789 gboolean
gerber_is_rs274x_p(gerb_file_t * fd,gboolean * returnFoundBinary)790 gerber_is_rs274x_p(gerb_file_t *fd, gboolean *returnFoundBinary)
791 {
792     char *buf;
793     int len = 0;
794     char *letter;
795     int i;
796     gboolean found_binary = FALSE;
797     gboolean found_ADD = FALSE;
798     gboolean found_D0 = FALSE;
799     gboolean found_D2 = FALSE;
800     gboolean found_M0 = FALSE;
801     gboolean found_M2 = FALSE;
802     gboolean found_star = FALSE;
803     gboolean found_X = FALSE;
804     gboolean found_Y = FALSE;
805 
806     dprintf ("%s(%p, %p), fd->fd = %p\n",
807 		    __func__, fd, returnFoundBinary, fd->fd);
808     buf = (char *) g_malloc(MAXL);
809     if (buf == NULL)
810 	GERB_FATAL_ERROR("malloc buf failed while checking for rs274x in %s()",
811 			__FUNCTION__);
812 
813     while (fgets(buf, MAXL, fd->fd) != NULL) {
814         dprintf ("buf = \"%s\"\n", buf);
815 	len = strlen(buf);
816 
817 	/* First look through the file for indications of its type by
818 	 * checking that file is not binary (non-printing chars and white
819 	 * spaces)
820 	 */
821 	for (i = 0; i < len; i++) {
822 	    if (!isprint((int) buf[i]) && (buf[i] != '\r') &&
823 		(buf[i] != '\n') && (buf[i] != '\t')) {
824 		found_binary = TRUE;
825                 dprintf ("found_binary (%d)\n", buf[i]);
826 	    }
827 	}
828 	if (g_strstr_len(buf, len, "%ADD")) {
829 	    found_ADD = TRUE;
830             dprintf ("found_ADD\n");
831 	}
832 	if (g_strstr_len(buf, len, "D00") || g_strstr_len(buf, len, "D0")) {
833 	    found_D0 = TRUE;
834             dprintf ("found_D0\n");
835 	}
836 	if (g_strstr_len(buf, len, "D02") || g_strstr_len(buf, len, "D2")) {
837 	    found_D2 = TRUE;
838             dprintf ("found_D2\n");
839 	}
840 	if (g_strstr_len(buf, len, "M00") || g_strstr_len(buf, len, "M0")) {
841 	    found_M0 = TRUE;
842             dprintf ("found_M0\n");
843 	}
844 	if (g_strstr_len(buf, len, "M02") || g_strstr_len(buf, len, "M2")) {
845 	    found_M2 = TRUE;
846             dprintf ("found_M2\n");
847 	}
848 	if (g_strstr_len(buf, len, "*")) {
849 	    found_star = TRUE;
850             dprintf ("found_star\n");
851 	}
852 	/* look for X<number> or Y<number> */
853 	if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
854 	    if (isdigit((int) letter[1])) { /* grab char after X */
855 		found_X = TRUE;
856                 dprintf ("found_X\n");
857 	    }
858 	}
859 	if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
860 	    if (isdigit((int) letter[1])) { /* grab char after Y */
861 		found_Y = TRUE;
862                 dprintf ("found_Y\n");
863 	    }
864 	}
865     }
866     rewind(fd->fd);
867     free(buf);
868 
869     *returnFoundBinary = found_binary;
870 
871     /* Now form logical expression determining if the file is RS-274X */
872     if ((found_D0 || found_D2 || found_M0 || found_M2) &&
873 	found_ADD && found_star && (found_X || found_Y))
874 	return TRUE;
875 
876 
877     return FALSE;
878 
879 } /* gerber_is_rs274x */
880 
881 
882 /* ------------------------------------------------------------------- */
883 /*! Checks for signs that this is a RS-274D file
884  *  Returns TRUE if it is, FALSE if not.
885  */
886 gboolean
gerber_is_rs274d_p(gerb_file_t * fd)887 gerber_is_rs274d_p(gerb_file_t *fd)
888 {
889     char *buf;
890     int len = 0;
891     char *letter;
892     int i;
893     gboolean found_binary = FALSE;
894     gboolean found_ADD = FALSE;
895     gboolean found_D0 = FALSE;
896     gboolean found_D2 = FALSE;
897     gboolean found_M0 = FALSE;
898     gboolean found_M2 = FALSE;
899     gboolean found_star = FALSE;
900     gboolean found_X = FALSE;
901     gboolean found_Y = FALSE;
902 
903     buf = malloc(MAXL);
904     if (buf == NULL)
905 	GERB_FATAL_ERROR("malloc buf failed while checking for rs274d in %s()",
906 			__FUNCTION__);
907 
908     while (fgets(buf, MAXL, fd->fd) != NULL) {
909 	len = strlen(buf);
910 
911 	/* First look through the file for indications of its type */
912 
913 	/* check that file is not binary (non-printing chars */
914 	for (i = 0; i < len; i++) {
915 	    if (!isprint( (int) buf[i]) && (buf[i] != '\r') &&
916 		(buf[i] != '\n') && (buf[i] != '\t')) {
917 		found_binary = TRUE;
918 	    }
919 	}
920 
921 	if (g_strstr_len(buf, len, "%ADD")) {
922 	    found_ADD = TRUE;
923 	}
924 	if (g_strstr_len(buf, len, "D00") || g_strstr_len(buf, len, "D0")) {
925 	    found_D0 = TRUE;
926 	}
927 	if (g_strstr_len(buf, len, "D02") || g_strstr_len(buf, len, "D2")) {
928 	    found_D2 = TRUE;
929 	}
930 	if (g_strstr_len(buf, len, "M00") || g_strstr_len(buf, len, "M0")) {
931 	    found_M0 = TRUE;
932 	}
933 	if (g_strstr_len(buf, len, "M02") || g_strstr_len(buf, len, "M2")) {
934 	    found_M2 = TRUE;
935 	}
936 	if (g_strstr_len(buf, len, "*")) {
937 	    found_star = TRUE;
938 	}
939 	/* look for X<number> or Y<number> */
940 	if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
941 	    /* grab char after X */
942 	    if (isdigit( (int) letter[1])) {
943 		found_X = TRUE;
944 	    }
945 	}
946 	if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
947 	    /* grab char after Y */
948 	    if (isdigit( (int) letter[1])) {
949 		found_Y = TRUE;
950 	    }
951 	}
952     }
953     rewind(fd->fd);
954     free(buf);
955 
956     /* Now form logical expression determining if the file is RS-274D */
957     if ((found_D0 || found_D2 || found_M0 || found_M2) &&
958 	!found_ADD && found_star && (found_X || found_Y) &&
959 	!found_binary)
960 	return TRUE;
961 
962     return FALSE;
963 
964 } /* gerber_is_rs274d */
965 
966 
967 /* ------------------------------------------------------------------- */
968 /*! This function reads a G number and updates the current
969  *  state.  It also updates the G stats counters
970  */
971 static void
parse_G_code(gerb_file_t * fd,gerb_state_t * state,gerbv_image_t * image,long int * line_num_p)972 parse_G_code(gerb_file_t *fd, gerb_state_t *state,
973 		gerbv_image_t *image, long int *line_num_p)
974 {
975     int  op_int;
976     gerbv_format_t *format = image->format;
977     gerbv_stats_t *stats = image->gerbv_stats;
978     gerbv_error_list_t *error_list = stats->error_list;
979     int c;
980 
981     op_int=gerb_fgetint(fd, NULL);
982 
983     /* Emphasize text with new line '\n' in the beginning */
984     dprintf("\n     Found G%02d at line %ld (%s)\n",
985 		    op_int, *line_num_p, gerber_g_code_name(op_int));
986 
987     switch(op_int) {
988     case 0:  /* Move */
989 	/* Is this doing anything really? */
990 	stats->G0++;
991 	break;
992     case 1:  /* Linear Interpolation (1X scale) */
993 	state->interpolation = GERBV_INTERPOLATION_LINEARx1;
994 	stats->G1++;
995 	break;
996     case 2:  /* Clockwise Linear Interpolation */
997 	state->interpolation = GERBV_INTERPOLATION_CW_CIRCULAR;
998 	stats->G2++;
999 	break;
1000     case 3:  /* Counter Clockwise Linear Interpolation */
1001 	state->interpolation = GERBV_INTERPOLATION_CCW_CIRCULAR;
1002 	stats->G3++;
1003 	break;
1004     case 4:  /* Ignore Data Block */
1005 	/* Don't do anything, just read 'til * */
1006         /* SDB asks:  Should we look for other codes while reading G04 in case
1007 	 * user forgot to put * at end of comment block? */
1008 	do {
1009 	    c = gerb_fgetc(fd);
1010 	}
1011 	while (c != EOF && c != '*');
1012 
1013 	stats->G4++;
1014 	break;
1015     case 10: /* Linear Interpolation (10X scale) */
1016 	state->interpolation = GERBV_INTERPOLATION_LINEARx10;
1017 	stats->G10++;
1018 	break;
1019     case 11: /* Linear Interpolation (0.1X scale) */
1020 	state->interpolation = GERBV_INTERPOLATION_LINEARx01;
1021 	stats->G11++;
1022 	break;
1023     case 12: /* Linear Interpolation (0.01X scale) */
1024 	state->interpolation = GERBV_INTERPOLATION_LINEARx001;
1025 	stats->G12++;
1026 	break;
1027     case 36: /* Turn on Polygon Area Fill */
1028 	state->prev_interpolation = state->interpolation;
1029 	state->interpolation = GERBV_INTERPOLATION_PAREA_START;
1030 	state->changed = 1;
1031 	stats->G36++;
1032 	break;
1033     case 37: /* Turn off Polygon Area Fill */
1034 	state->interpolation = GERBV_INTERPOLATION_PAREA_END;
1035 	state->changed = 1;
1036 	stats->G37++;
1037 	break;
1038     case 54: /* Tool prepare */
1039 	/* XXX Maybe uneccesary??? */
1040 	if (gerb_fgetc(fd) == 'D') {
1041 	    int a = gerb_fgetint(fd, NULL);
1042 	    if ((a >= 0) && (a <= APERTURE_MAX)) {
1043 		state->curr_aperture = a;
1044 	    } else {
1045 		gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1046 			_("Found aperture D%02d out of bounds while parsing "
1047 			    "G code at line %ld in file \"%s\""),
1048 			a, *line_num_p, fd->filename);
1049 	    }
1050 	} else {
1051 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1052 		    _("Found unexpected code after G54 "
1053 			"at line %ld in file \"%s\""),
1054 		    *line_num_p, fd->filename);
1055 /* TODO: insert error count here */
1056 	}
1057 	stats->G54++;
1058 	break;
1059     case 55: /* Prepare for flash */
1060 	stats->G55++;
1061 	break;
1062     case 70: /* Specify inches */
1063 	state->state = gerbv_image_return_new_netstate (state->state);
1064 	state->state->unit = GERBV_UNIT_INCH;
1065 	stats->G70++;
1066 	break;
1067     case 71: /* Specify millimeters */
1068 	state->state = gerbv_image_return_new_netstate (state->state);
1069 	state->state->unit = GERBV_UNIT_MM;
1070 	stats->G71++;
1071 	break;
1072     case 74: /* Disable 360 circular interpolation */
1073 	state->mq_on = 0;
1074 	stats->G74++;
1075 	break;
1076     case 75: /* Enable 360 circular interpolation */
1077 	state->mq_on = 1;
1078 	stats->G75++;
1079 	break;
1080     case 90: /* Specify absolut format */
1081 	if (format) format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1082 	stats->G90++;
1083 	break;
1084     case 91: /* Specify incremental format */
1085 	if (format) format->coordinate = GERBV_COORDINATE_INCREMENTAL;
1086 	stats->G91++;
1087 	break;
1088     default:
1089 	gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1090 		_("Encountered unknown G code G%02d "
1091 		    "at line %ld in file \"%s\""),
1092 		op_int, *line_num_p, fd->filename);
1093 	gerbv_stats_printf(error_list, GERBV_MESSAGE_WARNING, -1,
1094 		_("Ignorning unknown G code G%02d"), op_int);
1095 	stats->G_unknown++;
1096 /* TODO: insert error count here */
1097 
1098 	break;
1099     }
1100 
1101     return;
1102 } /* parse_G_code */
1103 
1104 
1105 /* ------------------------------------------------------------------ */
1106 /*! This function reads the numeric value of a D code and updates the
1107  *  state.  It also updates the D stats counters
1108  */
1109 static void
parse_D_code(gerb_file_t * fd,gerb_state_t * state,gerbv_image_t * image,long int * line_num_p)1110 parse_D_code(gerb_file_t *fd, gerb_state_t *state,
1111 		gerbv_image_t *image, long int *line_num_p)
1112 {
1113     int a;
1114     gerbv_stats_t *stats = image->gerbv_stats;
1115     gerbv_error_list_t *error_list = stats->error_list;
1116 
1117     a = gerb_fgetint(fd, NULL);
1118     dprintf("     Found D%02d code at line %ld\n", a, *line_num_p);
1119 
1120     switch(a) {
1121     case 0 : /* Invalid code */
1122         gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1123 		_("Found invalid D00 code at line %ld in file \"%s\""),
1124 		*line_num_p, fd->filename);
1125         stats->D_error++;
1126 	break;
1127     case 1 : /* Exposure on */
1128 	state->aperture_state = GERBV_APERTURE_STATE_ON;
1129 	state->changed = 1;
1130 	stats->D1++;
1131 	break;
1132     case 2 : /* Exposure off */
1133 	state->aperture_state = GERBV_APERTURE_STATE_OFF;
1134 	state->changed = 1;
1135 	stats->D2++;
1136 	break;
1137     case 3 : /* Flash aperture */
1138 	state->aperture_state = GERBV_APERTURE_STATE_FLASH;
1139 	state->changed = 1;
1140 	stats->D3++;
1141 	break;
1142     default: /* Aperture in use */
1143 	if ((a >= 0) && (a <= APERTURE_MAX)) {
1144 	    state->curr_aperture = a;
1145 
1146 	} else {
1147 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1148 		    _("Found out of bounds aperture D%02d "
1149 			"at line %ld in file \"%s\""),
1150 		    a, *line_num_p, fd->filename);
1151 	    stats->D_error++;
1152 	}
1153 	state->changed = 0;
1154 	break;
1155     }
1156 
1157     return;
1158 } /* parse_D_code */
1159 
1160 
1161 /* ------------------------------------------------------------------ */
1162 static int
parse_M_code(gerb_file_t * fd,gerbv_image_t * image,long int * line_num_p)1163 parse_M_code(gerb_file_t *fd, gerbv_image_t *image, long int *line_num_p)
1164 {
1165     int op_int;
1166     gerbv_stats_t *stats = image->gerbv_stats;
1167 
1168     op_int=gerb_fgetint(fd, NULL);
1169 
1170     switch (op_int) {
1171     case 0:  /* Program stop */
1172 	stats->M0++;
1173 	return 1;
1174     case 1:  /* Optional stop */
1175 	stats->M1++;
1176 	return 2;
1177     case 2:  /* End of program */
1178 	stats->M2++;
1179 	return 3;
1180     default:
1181 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_ERROR, -1,
1182 		_("Encountered unknown M%02d code at line %ld in file \"%s\""),
1183 		op_int, *line_num_p, fd->filename);
1184 	gerbv_stats_printf(stats->error_list, GERBV_MESSAGE_WARNING, -1,
1185 		_("Ignorning unknown M%02d code"), op_int);
1186 	stats->M_unknown++;
1187     }
1188     return 0;
1189 } /* parse_M_code */
1190 
1191 /* ------------------------------------------------------------------ */
1192 static void
parse_rs274x(gint levelOfRecursion,gerb_file_t * fd,gerbv_image_t * image,gerb_state_t * state,gerbv_net_t * curr_net,gerbv_stats_t * stats,gchar * directoryPath,long int * line_num_p)1193 parse_rs274x(gint levelOfRecursion, gerb_file_t *fd, gerbv_image_t *image,
1194 	     gerb_state_t *state, gerbv_net_t *curr_net, gerbv_stats_t *stats,
1195 	     gchar *directoryPath, long int *line_num_p)
1196 {
1197     int op[2];
1198     char str[3];
1199     int tmp;
1200     gerbv_aperture_t *a = NULL;
1201     gerbv_amacro_t *tmp_amacro;
1202     int ano;
1203     gdouble scale = 1.0;
1204     gerbv_error_list_t *error_list = stats->error_list;
1205 
1206     if (state->state->unit == GERBV_UNIT_MM)
1207     	scale = 25.4;
1208 
1209     op[0] = gerb_fgetc(fd);
1210     op[1] = gerb_fgetc(fd);
1211 
1212     if (op[0] == EOF || op[1] == EOF)
1213 	gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1214 		_("Unexpected EOF found in file \"%s\""), fd->filename);
1215 
1216     switch (A2I(op[0], op[1])){
1217 
1218 	/*
1219 	 * Directive parameters
1220 	 */
1221     case A2I('A','S'): /* Axis Select */
1222 	op[0] = gerb_fgetc(fd);
1223 	op[1] = gerb_fgetc(fd);
1224 	state->state = gerbv_image_return_new_netstate (state->state);
1225 
1226 	if (op[0] == EOF || op[1] == EOF)
1227 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1228 		    _("Unexpected EOF found in file \"%s\""), fd->filename);
1229 
1230 	if (((op[0] == 'A') && (op[1] == 'Y')) ||
1231 	    ((op[0] == 'B') && (op[1] == 'X'))) {
1232 	    state->state->axisSelect = GERBV_AXIS_SELECT_SWAPAB;
1233 	} else {
1234 	    state->state->axisSelect = GERBV_AXIS_SELECT_NOSELECT;
1235 	}
1236 
1237 	op[0] = gerb_fgetc(fd);
1238 	op[1] = gerb_fgetc(fd);
1239 
1240 	if (op[0] == EOF || op[1] == EOF)
1241 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1242 		    _("Unexpected EOF found in file \"%s\""), fd->filename);
1243 
1244 	if (((op[0] == 'A') && (op[1] == 'Y')) ||
1245 	    ((op[0] == 'B') && (op[1] == 'X'))) {
1246 	    state->state->axisSelect = GERBV_AXIS_SELECT_SWAPAB;
1247 	} else {
1248 	    state->state->axisSelect = GERBV_AXIS_SELECT_NOSELECT;
1249 	}
1250 	break;
1251 
1252     case A2I('F','S'): /* Format Statement */
1253 	image->format = g_new0 (gerbv_format_t,1);
1254 
1255 	switch (gerb_fgetc(fd)) {
1256 	case 'L':
1257 	    image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1258 	    break;
1259 	case 'T':
1260 	    image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
1261 	    break;
1262 	case 'D':
1263 	    image->format->omit_zeros = GERBV_OMIT_ZEROS_EXPLICIT;
1264 	    break;
1265 	default:
1266 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1267 		    _("EagleCad bug detected: Undefined handling of zeros "
1268 			"in format code at line %ld in file \"%s\""),
1269 		    *line_num_p, fd->filename);
1270 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_WARNING, -1,
1271 		    _("Defaulting to omitting leading zeros"));
1272 	    gerb_ungetc(fd);
1273 	    image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1274 	}
1275 
1276 	switch (gerb_fgetc(fd)) {
1277 	case 'A':
1278 	    image->format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1279 	    break;
1280 	case 'I':
1281 	    image->format->coordinate = GERBV_COORDINATE_INCREMENTAL;
1282 	    break;
1283 	default:
1284 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1285 		    _("Invalid coordinate type defined in format code "
1286 			"at line %ld in file \"%s\""),
1287 		    *line_num_p, fd->filename);
1288 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_WARNING, -1,
1289 		    _("Defaulting to absolute coordinates"));
1290 	    image->format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1291 	}
1292 	op[0] = gerb_fgetc(fd);
1293 	while((op[0] != '*')&&(op[0] != EOF)) {
1294 	    switch (op[0]) {
1295 	    case 'N':
1296 		op[0] = (char)gerb_fgetc(fd);
1297 		image->format->lim_seqno = op[0] - '0';
1298 		break;
1299 	    case 'G':
1300 		op[0] = (char)gerb_fgetc(fd);
1301 		image->format->lim_gf = op[0] - '0';
1302 		break;
1303 	    case 'D':
1304 		op[0] = (char)gerb_fgetc(fd);
1305 		image->format->lim_pf = op[0] - '0';
1306 		break;
1307 	    case 'M':
1308 		op[0] = (char)gerb_fgetc(fd);
1309 		image->format->lim_mf = op[0] - '0';
1310 		break;
1311 	    case 'X' :
1312 		op[0] = gerb_fgetc(fd);
1313 		if ((op[0] < '0') || (op[0] > '6')) {
1314 		    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1315 			    _("Illegal format size '%s' "
1316 				"at line %ld in file \"%s\""),
1317 			    gerbv_escape_char(op[0]),
1318 			    *line_num_p, fd->filename);
1319 		}
1320 		image->format->x_int = op[0] - '0';
1321 		op[0] = gerb_fgetc(fd);
1322 		if ((op[0] < '0') || (op[0] > '6')) {
1323 		    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1324 			    _("Illegal format size '%s' "
1325 				"at line %ld in file \"%s\""),
1326 			    gerbv_escape_char(op[0]),
1327 			    *line_num_p, fd->filename);
1328 		}
1329 		image->format->x_dec = op[0] - '0';
1330 		break;
1331 	    case 'Y':
1332 		op[0] = gerb_fgetc(fd);
1333 		if ((op[0] < '0') || (op[0] > '6')) {
1334 		    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1335 			    _("Illegal format size '%s' "
1336 				"at line %ld in file \"%s\""),
1337 			    gerbv_escape_char(op[0]),
1338 			    *line_num_p, fd->filename);
1339 		}
1340 		image->format->y_int = op[0] - '0';
1341 		op[0] = gerb_fgetc(fd);
1342 		if ((op[0] < '0') || (op[0] > '6')) {
1343 		    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1344 			    _("Illegal format size '%s' "
1345 			       "at line %ld in file \"%s\""),
1346 			    gerbv_escape_char(op[0]),
1347 			    *line_num_p, fd->filename);
1348 		}
1349 		image->format->y_dec = op[0] - '0';
1350 		break;
1351 	    default :
1352 		gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1353 			_("Illegal format statement '%s' "
1354 			   "at line %ld in file \"%s\""),
1355 			gerbv_escape_char(op[0]),
1356 			*line_num_p, fd->filename);
1357 		gerbv_stats_printf(error_list, GERBV_MESSAGE_WARNING, -1,
1358 			_("Ignoring invalid format statement"));
1359 	    }
1360 	    op[0] = gerb_fgetc(fd);
1361 	}
1362 	break;
1363     case A2I('M','I'): /* Mirror Image */
1364 	op[0] = gerb_fgetc(fd);
1365 	state->state = gerbv_image_return_new_netstate (state->state);
1366 
1367 	while ((op[0] != '*')&&(op[0] != EOF)) {
1368             gint readValue=0;
1369 	    switch (op[0]) {
1370 	    case 'A' :
1371 		readValue = gerb_fgetint(fd, NULL);
1372 		if (readValue == 1) {
1373 		    if (state->state->mirrorState == GERBV_MIRROR_STATE_FLIPB)
1374 			state->state->mirrorState=GERBV_MIRROR_STATE_FLIPAB;
1375 		    else
1376 			state->state->mirrorState=GERBV_MIRROR_STATE_FLIPA;
1377 		}
1378 		break;
1379 	    case 'B' :
1380 		readValue = gerb_fgetint(fd, NULL);
1381 		if (readValue == 1) {
1382 		    if (state->state->mirrorState == GERBV_MIRROR_STATE_FLIPA)
1383 			state->state->mirrorState=GERBV_MIRROR_STATE_FLIPAB;
1384 		    else
1385 			state->state->mirrorState=GERBV_MIRROR_STATE_FLIPB;
1386 		}
1387 		break;
1388 	    default :
1389 		gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1390 			_("Wrong character '%s' in mirror "
1391 			    "at line %ld in file \"%s\""),
1392 			gerbv_escape_char(op[0]), *line_num_p, fd->filename);
1393 	    }
1394 	    op[0] = gerb_fgetc(fd);
1395 	}
1396 	break;
1397     case A2I('M','O'): /* Mode of Units */
1398 	op[0] = gerb_fgetc(fd);
1399 	op[1] = gerb_fgetc(fd);
1400 
1401 	if (op[0] == EOF || op[1] == EOF)
1402 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1403 		    _("Unexpected EOF found in file \"%s\""), fd->filename);
1404 
1405 	switch (A2I(op[0],op[1])) {
1406 	case A2I('I','N'):
1407 	    state->state = gerbv_image_return_new_netstate (state->state);
1408 	    state->state->unit = GERBV_UNIT_INCH;
1409 	    break;
1410 	case A2I('M','M'):
1411 	    state->state = gerbv_image_return_new_netstate (state->state);
1412 	    state->state->unit = GERBV_UNIT_MM;
1413 	    break;
1414 	default:
1415 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1416 		    _("Illegal unit '%s%s' at line %ld in file \"%s\""),
1417 		    gerbv_escape_char(op[0]), gerbv_escape_char(op[1]),
1418 		    *line_num_p, fd->filename);
1419 	}
1420 	break;
1421     case A2I('O','F'): /* Offset */
1422 	op[0] = gerb_fgetc(fd);
1423 
1424 	while ((op[0] != '*')&&(op[0] != EOF)) {
1425 	    switch (op[0]) {
1426 	    case 'A' :
1427 		state->state->offsetA = gerb_fgetdouble(fd) / scale;
1428 		break;
1429 	    case 'B' :
1430 		state->state->offsetB = gerb_fgetdouble(fd) / scale;
1431 		break;
1432 	    default :
1433 		gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1434 			_("Wrong character '%s' in offset "
1435 			    "at line %ld in file \"%s\""),
1436 			gerbv_escape_char(op[0]), *line_num_p, fd->filename);
1437 	    }
1438 	    op[0] = gerb_fgetc(fd);
1439 	}
1440 	break;
1441     case A2I('I','F'): /* Include file */
1442 	{
1443 	    gchar *includeFilename = gerb_fgetstring(fd, '*');
1444 
1445 	    if (includeFilename) {
1446 		gchar *fullPath;
1447 		if (!g_path_is_absolute(includeFilename)) {
1448 		    fullPath = g_build_filename (directoryPath, includeFilename, NULL);
1449 		} else {
1450 		    fullPath = g_strdup (includeFilename);
1451 		}
1452 		if (levelOfRecursion < 10) {
1453 		    gerb_file_t *includefd = NULL;
1454 
1455 		    includefd = gerb_fopen(fullPath);
1456 		    if (includefd) {
1457 			gerber_parse_file_segment (levelOfRecursion + 1, image, state, curr_net, stats, includefd, directoryPath);
1458 			gerb_fclose(includefd);
1459 		    } else {
1460 			gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1461 				_("Included file \"%s\" cannot be found "
1462 				    "at line %ld in file \"%s\""),
1463 				fullPath, *line_num_p, fd->filename);
1464 		    }
1465 		    g_free (fullPath);
1466 		} else {
1467 		    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1468 			    _("Parser encountered more than 10 levels of "
1469 				"include file recursion which is not allowed "
1470 				"by the RS-274X spec"));
1471 		}
1472 
1473 	    }
1474 	}
1475 	break;
1476     case A2I('I','O'): /* Image offset */
1477 	op[0] = gerb_fgetc(fd);
1478 
1479 	while ((op[0] != '*')&&(op[0] != EOF)) {
1480 	    switch (op[0]) {
1481 	    case 'A' :
1482 		image->info->offsetA = gerb_fgetdouble(fd) / scale;
1483 		break;
1484 	    case 'B' :
1485 		image->info->offsetB = gerb_fgetdouble(fd) / scale;
1486 		break;
1487 	    default :
1488 		gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1489 			_("Wrong character '%s' in image offset "
1490 			    "at line %ld in file \"%s\""),
1491 			gerbv_escape_char(op[0]), *line_num_p, fd->filename);
1492 	    }
1493 	    op[0] = gerb_fgetc(fd);
1494 	}
1495 	break;
1496     case A2I('S','F'): /* Scale Factor */
1497      state->state = gerbv_image_return_new_netstate (state->state);
1498 	if (gerb_fgetc(fd) == 'A')
1499 	    state->state->scaleA = gerb_fgetdouble(fd);
1500 	else
1501 	    gerb_ungetc(fd);
1502 	if (gerb_fgetc(fd) == 'B')
1503 	    state->state->scaleB = gerb_fgetdouble(fd);
1504 	else
1505 	    gerb_ungetc(fd);
1506 	break;
1507     case A2I('I','C'): /* Input Code */
1508 	/* Thanks to Stephen Adam for providing this information. As he writes:
1509 	 *      btw, here's a logic puzzle for you.  If you need to
1510 	 * read the gerber file to see how it's encoded, then
1511 	 * how can you read it?
1512 	 */
1513 	op[0] = gerb_fgetc(fd);
1514 	op[1] = gerb_fgetc(fd);
1515 
1516 	if (op[0] == EOF || op[1] == EOF)
1517 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1518 		    _("Unexpected EOF found in file \"%s\""), fd->filename);
1519 
1520 	switch (A2I(op[0],op[1])) {
1521 	case A2I('A','S'):
1522 	    image->info->encoding = GERBV_ENCODING_ASCII;
1523 	    break;
1524 	case A2I('E','B'):
1525 	    image->info->encoding = GERBV_ENCODING_EBCDIC;
1526 	    break;
1527 	case A2I('B','C'):
1528 	    image->info->encoding = GERBV_ENCODING_BCD;
1529 	    break;
1530 	case A2I('I','S'):
1531 	    image->info->encoding = GERBV_ENCODING_ISO_ASCII;
1532 	    break;
1533 	case A2I('E','I'):
1534 	    image->info->encoding = GERBV_ENCODING_EIA;
1535 	    break;
1536 	default:
1537 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1538 		    _("Unknown input code (IC) '%s%s' "
1539 			"at line %ld in file \"%s\""),
1540 		    gerbv_escape_char(op[0]), gerbv_escape_char(op[1]),
1541 		    *line_num_p, fd->filename);
1542 	}
1543 	break;
1544 
1545 	/* Image parameters */
1546     case A2I('I','J'): /* Image Justify */
1547 	op[0] = gerb_fgetc(fd);
1548 	image->info->imageJustifyTypeA = GERBV_JUSTIFY_LOWERLEFT;
1549 	image->info->imageJustifyTypeB = GERBV_JUSTIFY_LOWERLEFT;
1550 	image->info->imageJustifyOffsetA = 0.0;
1551 	image->info->imageJustifyOffsetB = 0.0;
1552 	while ((op[0] != '*')&&(op[0] != EOF)) {
1553 	    switch (op[0]) {
1554 	    case 'A' :
1555 	    	op[0] = gerb_fgetc(fd);
1556 	    	if (op[0] == 'C') {
1557 		    image->info->imageJustifyTypeA = GERBV_JUSTIFY_CENTERJUSTIFY;
1558 	    	} else if (op[0] == 'L') {
1559 		    image->info->imageJustifyTypeA = GERBV_JUSTIFY_LOWERLEFT;
1560 	    	} else {
1561 		    gerb_ungetc (fd);
1562 		    image->info->imageJustifyOffsetA = gerb_fgetdouble(fd) / scale;
1563 	    	}
1564 		break;
1565 	    case 'B' :
1566 		op[0] = gerb_fgetc(fd);
1567 	    	if (op[0] == 'C') {
1568 		    image->info->imageJustifyTypeB = GERBV_JUSTIFY_CENTERJUSTIFY;
1569 	    	} else if (op[0] == 'L') {
1570 		    image->info->imageJustifyTypeB = GERBV_JUSTIFY_LOWERLEFT;
1571 	    	} else {
1572 		    gerb_ungetc (fd);
1573 		    image->info->imageJustifyOffsetB = gerb_fgetdouble(fd) / scale;
1574 	    	}
1575 		break;
1576 	    default :
1577 		gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1578 			_("Wrong character '%s' in image justify "
1579 			    "at line %ld in file \"%s\""),
1580 			gerbv_escape_char(op[0]), *line_num_p, fd->filename);
1581 	    }
1582 	    op[0] = gerb_fgetc(fd);
1583 	}
1584 	break;
1585     case A2I('I','N'): /* Image Name */
1586 	image->info->name = gerb_fgetstring(fd, '*');
1587 	break;
1588     case A2I('I','P'): /* Image Polarity */
1589 
1590 	for (ano = 0; ano < 3; ano++) {
1591 	    op[0] = gerb_fgetc(fd);
1592 	    if (op[0] == EOF) {
1593 		gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1594 			_("Unexpected EOF while reading image polarity (IP) "
1595 			    "in file \"%s\""), fd->filename);
1596 	    }
1597 	    str[ano] = (char)op[0];
1598 	}
1599 
1600 	if (strncmp(str, "POS", 3) == 0)
1601 	    image->info->polarity = GERBV_POLARITY_POSITIVE;
1602 	else if (strncmp(str, "NEG", 3) == 0)
1603 	    image->info->polarity = GERBV_POLARITY_NEGATIVE;
1604 	else {
1605 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1606 		    _("Unknown polarity '%s%s%s' "
1607 			"at line %ld in file \"%s\""),
1608 		    gerbv_escape_char(str[0]), gerbv_escape_char(str[1]),
1609 		    gerbv_escape_char(str[2]), *line_num_p, fd->filename);
1610 	}
1611 	break;
1612     case A2I('I','R'): /* Image Rotation */
1613 	tmp = gerb_fgetint(fd, NULL) % 360;
1614 	if (tmp == 0)
1615 	    image->info->imageRotation = 0.0;
1616 	else if (tmp == 90)
1617 	    image->info->imageRotation = M_PI_2;
1618 	else if (tmp == 180)
1619 	    image->info->imageRotation = M_PI;
1620 	else if (tmp == 270)
1621 	    image->info->imageRotation = M_PI + M_PI_2;
1622 	else {
1623 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1624 		    _("Image rotation must be 0, 90, 180 or 270 "
1625 			"(is actually %d) at line %ld in file \"%s\""),
1626 		    tmp, *line_num_p, fd->filename);
1627 	}
1628 	break;
1629     case A2I('P','F'): /* Plotter Film */
1630 	image->info->plotterFilm = gerb_fgetstring(fd, '*');
1631 	break;
1632 
1633 	/* Aperture parameters */
1634     case A2I('A','D'): /* Aperture Description */
1635 	a = (gerbv_aperture_t *) g_new0 (gerbv_aperture_t,1);
1636 
1637 	ano = parse_aperture_definition(fd, a, image, scale, line_num_p);
1638 	if (ano == -1) {
1639 		/* error with line parse, so just quietly ignore */
1640 	}
1641 	else if ((ano >= 0) && (ano <= APERTURE_MAX)) {
1642 	    a->unit = state->state->unit;
1643 	    image->aperture[ano] = a;
1644 	    dprintf("     In %s(), adding new aperture to aperture list ...\n",
1645 			    __func__);
1646 	    gerbv_stats_add_aperture(stats->aperture_list,
1647 				    -1, ano,
1648 				    a->type,
1649 				    a->parameter);
1650 	    gerbv_stats_add_to_D_list(stats->D_code_list,
1651 				     ano);
1652 	    if (ano < APERTURE_MIN) {
1653 		    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1654 			    _("Aperture number out of bounds %d "
1655 				"at line %ld in file \"%s\""),
1656 			    ano, *line_num_p, fd->filename);
1657 	    }
1658 	} else {
1659 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1660 		    _("Aperture number out of bounds %d "
1661 		       "at line %ld in file \"%s\""),
1662 		    ano, *line_num_p, fd->filename);
1663 	}
1664 	/* Add aperture info to stats->aperture_list here */
1665 
1666 	break;
1667     case A2I('A','M'): /* Aperture Macro */
1668 	tmp_amacro = image->amacro;
1669 	image->amacro = parse_aperture_macro(fd);
1670 	if (image->amacro) {
1671 	    image->amacro->next = tmp_amacro;
1672 #ifdef AMACRO_DEBUG
1673 	    print_program(image->amacro);
1674 #endif
1675 	} else {
1676 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1677 		    _("Failed to parse aperture macro "
1678 		       "at line %ld in file \"%s\""),
1679 		    *line_num_p, fd->filename);
1680 	}
1681 	// return, since we want to skip the later back-up loop
1682 	return;
1683 	/* Layer */
1684     case A2I('L','N'): /* Layer Name */
1685 	state->layer = gerbv_image_return_new_layer (state->layer);
1686 	state->layer->name = gerb_fgetstring(fd, '*');
1687 	break;
1688     case A2I('L','P'): /* Layer Polarity */
1689 	state->layer = gerbv_image_return_new_layer (state->layer);
1690 	switch (gerb_fgetc(fd)) {
1691 	case 'D': /* Dark Polarity (default) */
1692 	    state->layer->polarity = GERBV_POLARITY_DARK;
1693 	    break;
1694 	case 'C': /* Clear Polarity */
1695 	    state->layer->polarity = GERBV_POLARITY_CLEAR;
1696 	    break;
1697 	default:
1698 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1699 		    _("Unknown layer polarity '%s' "
1700 		       "at line %ld in file \"%s\""),
1701 		    gerbv_escape_char(op[0]), *line_num_p, fd->filename);
1702 	}
1703 	break;
1704     case A2I('K','O'): /* Knock Out */
1705         state->layer = gerbv_image_return_new_layer (state->layer);
1706         gerber_update_any_running_knockout_measurements (image);
1707         /* reset any previous knockout measurements */
1708         knockoutMeasure = FALSE;
1709         op[0] = gerb_fgetc(fd);
1710 	if (op[0] == '*') { /* Disable previous SR parameters */
1711 	    state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_NOKNOCKOUT;
1712 	    break;
1713 	} else if (op[0] == 'C') {
1714 	    state->layer->knockout.polarity = GERBV_POLARITY_CLEAR;
1715 	} else if (op[0] == 'D') {
1716 	    state->layer->knockout.polarity = GERBV_POLARITY_DARK;
1717 	} else {
1718 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1719 		    _("Knockout must supply a polarity (C, D, or *) "
1720 			"at line %ld in file \"%s\""),
1721 		    *line_num_p, fd->filename);
1722 	}
1723 	state->layer->knockout.lowerLeftX = 0.0;
1724 	state->layer->knockout.lowerLeftY = 0.0;
1725 	state->layer->knockout.width = 0.0;
1726 	state->layer->knockout.height = 0.0;
1727 	state->layer->knockout.border = 0.0;
1728 	state->layer->knockout.firstInstance = TRUE;
1729 	op[0] = gerb_fgetc(fd);
1730 	while ((op[0] != '*')&&(op[0] != EOF)) {
1731 	    switch (op[0]) {
1732 	    case 'X':
1733 	        state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1734 		state->layer->knockout.lowerLeftX = gerb_fgetdouble(fd) / scale;
1735 		break;
1736 	    case 'Y':
1737 	        state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1738 		state->layer->knockout.lowerLeftY = gerb_fgetdouble(fd) / scale;
1739 		break;
1740 	    case 'I':
1741 	        state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1742 		state->layer->knockout.width = gerb_fgetdouble(fd) / scale;
1743 		break;
1744 	    case 'J':
1745 	        state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1746 		state->layer->knockout.height = gerb_fgetdouble(fd) / scale;
1747 		break;
1748 	    case 'K':
1749 	        state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_BORDER;
1750 	        state->layer->knockout.border = gerb_fgetdouble(fd) / scale;
1751 	        /* this is a bordered knockout, so we need to start measuring the
1752 	           size of a square bordering all future components */
1753 	        knockoutMeasure = TRUE;
1754 	        knockoutLimitXmin = HUGE_VAL;
1755 	        knockoutLimitYmin = HUGE_VAL;
1756 	        knockoutLimitXmax = -HUGE_VAL;
1757 	        knockoutLimitYmax = -HUGE_VAL;
1758 	        knockoutLayer = state->layer;
1759 	        break;
1760 	    default:
1761 		gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1762 			_("Unknown variable in knockout "
1763 			    "at line %ld in file \"%s\""),
1764 			*line_num_p, fd->filename);
1765 	    }
1766 	    op[0] = gerb_fgetc(fd);
1767 	}
1768 	break;
1769     case A2I('S','R'): /* Step and Repeat */
1770         /* start by generating a new layer (duplicating previous layer settings */
1771         state->layer = gerbv_image_return_new_layer (state->layer);
1772 	op[0] = gerb_fgetc(fd);
1773 	if (op[0] == '*') { /* Disable previous SR parameters */
1774 	    state->layer->stepAndRepeat.X = 1;
1775 	    state->layer->stepAndRepeat.Y = 1;
1776 	    state->layer->stepAndRepeat.dist_X = 0.0;
1777 	    state->layer->stepAndRepeat.dist_Y = 0.0;
1778 	    break;
1779 	}
1780 	while ((op[0] != '*')&&(op[0] != EOF)) {
1781 	    switch (op[0]) {
1782 	    case 'X':
1783 		state->layer->stepAndRepeat.X = gerb_fgetint(fd, NULL);
1784 		break;
1785 	    case 'Y':
1786 		state->layer->stepAndRepeat.Y = gerb_fgetint(fd, NULL);
1787 		break;
1788 	    case 'I':
1789 		state->layer->stepAndRepeat.dist_X = gerb_fgetdouble(fd) / scale;
1790 		break;
1791 	    case 'J':
1792 		state->layer->stepAndRepeat.dist_Y = gerb_fgetdouble(fd) / scale;
1793 		break;
1794 	    default:
1795 		gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1796 			_("Step-and-repeat parameter error "
1797 			   "at line %ld in file \"%s\""),
1798 			*line_num_p, fd->filename);
1799 	    }
1800 
1801 	    /*
1802 	     * Repeating 0 times in any direction would disable the whole plot, and
1803 	     * is probably not intended. At least one other tool (viewmate) seems
1804 	     * to interpret 0-time repeating as repeating just once too.
1805 	     */
1806 	    if(state->layer->stepAndRepeat.X == 0)
1807 		state->layer->stepAndRepeat.X = 1;
1808 	    if(state->layer->stepAndRepeat.Y == 0)
1809 		state->layer->stepAndRepeat.Y = 1;
1810 
1811 	    op[0] = gerb_fgetc(fd);
1812 	}
1813 	break;
1814 	/* is this an actual RS274X command??  It isn't explainined in the spec... */
1815     case A2I('R','O'):
1816 	state->layer = gerbv_image_return_new_layer (state->layer);
1817 
1818 	state->layer->rotation = DEG2RAD(gerb_fgetdouble(fd));
1819 	op[0] = gerb_fgetc(fd);
1820 	if (op[0] != '*') {
1821 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1822 		    _("Error in layer rotation command "
1823 		       "at line %ld in file \"%s\""),
1824 		    *line_num_p, fd->filename);
1825 	}
1826 	break;
1827     default:
1828 	gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
1829 		_("Unknown RS-274X extension found %%%s%s%% "
1830 		    "at line %ld in file \"%s\""),
1831 		gerbv_escape_char(op[0]), gerbv_escape_char(op[1]),
1832 		*line_num_p, fd->filename);
1833     }
1834 
1835     // make sure we read until the trailing * character
1836     // first, backspace once in case we already read the trailing *
1837     gerb_ungetc(fd);
1838     do {
1839     	tmp = gerb_fgetc(fd);
1840     } while (tmp != EOF && tmp != '*');
1841 
1842     return;
1843 } /* parse_rs274x */
1844 
1845 
1846 /*
1847  * Stack declarations and operations to be used by the simple engine that
1848  * executes the parsed aperture macros.
1849  */
1850 typedef struct {
1851     double *stack;
1852     int sp;
1853 } macro_stack_t;
1854 
1855 
1856 static macro_stack_t *
new_stack(unsigned int stack_size)1857 new_stack(unsigned int stack_size)
1858 {
1859     macro_stack_t *s;
1860 
1861     s = (macro_stack_t *) g_new0 (macro_stack_t,1);
1862     s->stack = (double *) g_new0 (double, stack_size);
1863     s->sp = 0;
1864     return s;
1865 } /* new_stack */
1866 
1867 
1868 static void
free_stack(macro_stack_t * s)1869 free_stack(macro_stack_t *s)
1870 {
1871     if (s && s->stack)
1872 	free(s->stack);
1873 
1874     if (s)
1875 	free(s);
1876 
1877     return;
1878 } /* free_stack */
1879 
1880 
1881 static void
push(macro_stack_t * s,double val)1882 push(macro_stack_t *s, double val)
1883 {
1884     s->stack[s->sp++] = val;
1885     return;
1886 } /* push */
1887 
1888 
1889 static int
pop(macro_stack_t * s,double * value)1890 pop(macro_stack_t *s, double *value)
1891 {
1892     /* Check if we try to pop an empty stack */
1893     if (s->sp == 0) {
1894 	return -1;
1895     }
1896 
1897     *value = s->stack[--s->sp];
1898     return 0;
1899 } /* pop */
1900 
1901 
1902 /* ------------------------------------------------------------------ */
1903 static int
simplify_aperture_macro(gerbv_aperture_t * aperture,gdouble scale)1904 simplify_aperture_macro(gerbv_aperture_t *aperture, gdouble scale)
1905 {
1906     const int extra_stack_size = 10;
1907     macro_stack_t *s;
1908     gerbv_instruction_t *ip;
1909     int handled = 1, nuf_parameters = 0, i, j, clearOperatorUsed = FALSE;
1910     double *lp; /* Local copy of parameters */
1911     double tmp[2] = {0.0, 0.0};
1912     gerbv_aperture_type_t type = GERBV_APTYPE_NONE;
1913     gerbv_simplified_amacro_t *sam;
1914 
1915     if (aperture == NULL)
1916 	GERB_FATAL_ERROR(_("aperture NULL in simplify aperture macro"));
1917 
1918     if (aperture->amacro == NULL)
1919 	GERB_FATAL_ERROR(_("aperture->amacro NULL in simplify aperture macro"));
1920 
1921     /* Allocate stack for VM */
1922     s = new_stack(aperture->amacro->nuf_push + extra_stack_size);
1923     if (s == NULL)
1924 	GERB_FATAL_ERROR("malloc stack failed in %s()", __FUNCTION__);
1925 
1926     /* Make a copy of the parameter list that we can rewrite if necessary */
1927     lp = g_new (double,APERTURE_PARAMETERS_MAX);
1928 
1929     memcpy(lp, aperture->parameter, sizeof(double) * APERTURE_PARAMETERS_MAX);
1930 
1931     for(ip = aperture->amacro->program; ip != NULL; ip = ip->next) {
1932 	switch(ip->opcode) {
1933 	case GERBV_OPCODE_NOP:
1934 	    break;
1935 	case GERBV_OPCODE_PUSH :
1936 	    push(s, ip->data.fval);
1937 	    break;
1938         case GERBV_OPCODE_PPUSH :
1939 	    push(s, lp[ip->data.ival - 1]);
1940 	    break;
1941 	case GERBV_OPCODE_PPOP:
1942 	    if (pop(s, &tmp[0]) < 0)
1943 		GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
1944 	    lp[ip->data.ival - 1] = tmp[0];
1945 	    break;
1946 	case GERBV_OPCODE_ADD :
1947 	    if (pop(s, &tmp[0]) < 0)
1948 		GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
1949 	    if (pop(s, &tmp[1]) < 0)
1950 		GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
1951 	    push(s, tmp[1] + tmp[0]);
1952 	    break;
1953 	case GERBV_OPCODE_SUB :
1954 	    if (pop(s, &tmp[0]) < 0)
1955 		GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
1956 	    if (pop(s, &tmp[1]) < 0)
1957 		GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
1958 	    push(s, tmp[1] - tmp[0]);
1959 	    break;
1960 	case GERBV_OPCODE_MUL :
1961 	    if (pop(s, &tmp[0]) < 0)
1962 		GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
1963 	    if (pop(s, &tmp[1]) < 0)
1964 		GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
1965 	    push(s, tmp[1] * tmp[0]);
1966 	    break;
1967 	case GERBV_OPCODE_DIV :
1968 	    if (pop(s, &tmp[0]) < 0)
1969 		GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
1970 	    if (pop(s, &tmp[1]) < 0)
1971 		GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
1972 	    push(s, tmp[1] / tmp[0]);
1973 	    break;
1974 	case GERBV_OPCODE_PRIM :
1975 	    /*
1976 	     * This handles the exposure thing in the aperture macro
1977 	     * The exposure is always the first element on stack independent
1978 	     * of aperture macro.
1979 	     */
1980 	    switch(ip->data.ival) {
1981 	    case 1:
1982 		dprintf("  Aperture macro circle [1] (");
1983 		type = GERBV_APTYPE_MACRO_CIRCLE;
1984 		nuf_parameters = 4;
1985 		break;
1986 	    case 3:
1987 		break;
1988 	    case 4 :
1989 		dprintf("  Aperture macro outline [4] (");
1990 		type = GERBV_APTYPE_MACRO_OUTLINE;
1991 		/*
1992 		 * Number of parameters are:
1993 		 * - number of points defined in entry 1 of the stack +
1994 		 *   start point. Times two since it is both X and Y.
1995 		 * - Then three more; exposure,  nuf points and rotation.
1996 		 */
1997 		nuf_parameters = ((int)s->stack[1] + 1) * 2 + 3;
1998 		break;
1999 	    case 5 :
2000 		dprintf("  Aperture macro polygon [5] (");
2001 		type = GERBV_APTYPE_MACRO_POLYGON;
2002 		nuf_parameters = 6;
2003 		break;
2004 	    case 6 :
2005 		dprintf("  Aperture macro moire [6] (");
2006 		type = GERBV_APTYPE_MACRO_MOIRE;
2007 		nuf_parameters = 9;
2008 		break;
2009 	    case 7 :
2010 		dprintf("  Aperture macro thermal [7] (");
2011 		type = GERBV_APTYPE_MACRO_THERMAL;
2012 		nuf_parameters = 6;
2013 		break;
2014 	    case 2  :
2015 	    case 20 :
2016 		dprintf("  Aperture macro line 20/2 (");
2017 		type = GERBV_APTYPE_MACRO_LINE20;
2018 		nuf_parameters = 7;
2019 		break;
2020 	    case 21 :
2021 		dprintf("  Aperture macro line 21 (");
2022 		type = GERBV_APTYPE_MACRO_LINE21;
2023 		nuf_parameters = 6;
2024 		break;
2025 	    case 22 :
2026 		dprintf("  Aperture macro line 22 (");
2027 		type = GERBV_APTYPE_MACRO_LINE22;
2028 		nuf_parameters = 6;
2029 		break;
2030 	    default :
2031 		handled = 0;
2032 	    }
2033 
2034 	    if (type != GERBV_APTYPE_NONE) {
2035 		if (nuf_parameters > APERTURE_PARAMETERS_MAX) {
2036 			GERB_COMPILE_ERROR(_("Number of parameters to aperture macro (%d) "
2037 							"are more than gerbv is able to store (%d)"),
2038 							nuf_parameters, APERTURE_PARAMETERS_MAX);
2039 			nuf_parameters = APERTURE_PARAMETERS_MAX;
2040 		}
2041 
2042 		/*
2043 		 * Create struct for simplified aperture macro and
2044 		 * start filling in the blanks.
2045 		 */
2046 		sam = g_new (gerbv_simplified_amacro_t, 1);
2047 		sam->type = type;
2048 		sam->next = NULL;
2049 		memset(sam->parameter, 0,
2050 		       sizeof(double) * APERTURE_PARAMETERS_MAX);
2051 		memcpy(sam->parameter, s->stack,
2052 		       sizeof(double) *  nuf_parameters);
2053 
2054 		/* convert any mm values to inches */
2055 		switch (type) {
2056 		    case GERBV_APTYPE_MACRO_CIRCLE:
2057 			if (fabs(sam->parameter[0]) < 0.001)
2058 			    clearOperatorUsed = TRUE;
2059 			sam->parameter[1]/=scale;
2060 			sam->parameter[2]/=scale;
2061 			sam->parameter[3]/=scale;
2062 			break;
2063 		    case GERBV_APTYPE_MACRO_OUTLINE:
2064 			if (fabs(sam->parameter[0]) < 0.001)
2065 			    clearOperatorUsed = TRUE;
2066 			for (j=2; j<nuf_parameters-1; j++){
2067 			    sam->parameter[j]/=scale;
2068 			}
2069 			break;
2070 		    case GERBV_APTYPE_MACRO_POLYGON:
2071 			if (fabs(sam->parameter[0]) < 0.001)
2072 			    clearOperatorUsed = TRUE;
2073 			sam->parameter[2]/=scale;
2074 			sam->parameter[3]/=scale;
2075 			sam->parameter[4]/=scale;
2076 			break;
2077 		    case GERBV_APTYPE_MACRO_MOIRE:
2078 			sam->parameter[0]/=scale;
2079 			sam->parameter[1]/=scale;
2080 			sam->parameter[2]/=scale;
2081 			sam->parameter[3]/=scale;
2082 			sam->parameter[4]/=scale;
2083 			sam->parameter[6]/=scale;
2084 			sam->parameter[7]/=scale;
2085 			break;
2086 		    case GERBV_APTYPE_MACRO_THERMAL:
2087 			sam->parameter[0]/=scale;
2088 			sam->parameter[1]/=scale;
2089 			sam->parameter[2]/=scale;
2090 			sam->parameter[3]/=scale;
2091 			sam->parameter[4]/=scale;
2092 			break;
2093 		    case GERBV_APTYPE_MACRO_LINE20:
2094 			if (fabs(sam->parameter[0]) < 0.001)
2095 			    clearOperatorUsed = TRUE;
2096 			sam->parameter[1]/=scale;
2097 			sam->parameter[2]/=scale;
2098 			sam->parameter[3]/=scale;
2099 			sam->parameter[4]/=scale;
2100 			sam->parameter[5]/=scale;
2101 			break;
2102 		    case GERBV_APTYPE_MACRO_LINE21:
2103 		    case GERBV_APTYPE_MACRO_LINE22:
2104 			if (fabs(sam->parameter[0]) < 0.001)
2105 			    clearOperatorUsed = TRUE;
2106 			sam->parameter[1]/=scale;
2107 			sam->parameter[2]/=scale;
2108 			sam->parameter[3]/=scale;
2109 			sam->parameter[4]/=scale;
2110 			break;
2111 		    default:
2112 			break;
2113 		}
2114 		/*
2115 		 * Add this simplified aperture macro to the end of the list
2116 		 * of simplified aperture macros. If first entry, put it
2117 		 * in the top.
2118 		 */
2119 		if (aperture->simplified == NULL) {
2120 		    aperture->simplified = sam;
2121 		} else {
2122 		    gerbv_simplified_amacro_t *tmp_sam;
2123 		    tmp_sam = aperture->simplified;
2124 		    while (tmp_sam->next != NULL) {
2125 			tmp_sam = tmp_sam->next;
2126 		    }
2127 		    tmp_sam->next = sam;
2128 		}
2129 
2130 #ifdef DEBUG
2131 		for (i = 0; i < nuf_parameters; i++) {
2132 		    dprintf("%f, ", s->stack[i]);
2133 		}
2134 #endif /* DEBUG */
2135 		dprintf(")\n");
2136 	    }
2137 
2138 	    /*
2139 	     * Here we reset the stack pointer. It's not general correct
2140 	     * correct to do this, but since I know how the compiler works
2141 	     * I can do this. The correct way to do this should be to
2142 	     * subtract number of used elements in each primitive operation.
2143 	     */
2144 	    s->sp = 0;
2145 	    break;
2146 	default :
2147 	    break;
2148 	}
2149     }
2150     free_stack(s);
2151     g_free (lp);
2152 
2153     /* store a flag to let the renderer know if it should expect any "clear"
2154        primatives */
2155     aperture->parameter[0]= (gdouble) clearOperatorUsed;
2156     return handled;
2157 } /* simplify_aperture_macro */
2158 
2159 
2160 /* ------------------------------------------------------------------ */
2161 static int
parse_aperture_definition(gerb_file_t * fd,gerbv_aperture_t * aperture,gerbv_image_t * image,gdouble scale,long int * line_num_p)2162 parse_aperture_definition(gerb_file_t *fd, gerbv_aperture_t *aperture,
2163 			  gerbv_image_t *image, gdouble scale,
2164 			  long int *line_num_p)
2165 {
2166     int ano, i;
2167     char *ad;
2168     char *token;
2169     gerbv_amacro_t *curr_amacro;
2170     gerbv_amacro_t *amacro = image->amacro;
2171     gerbv_error_list_t *error_list = image->gerbv_stats->error_list;
2172     gdouble tempHolder;
2173 
2174     if (gerb_fgetc(fd) != 'D') {
2175 	gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
2176 		_("Found AD code with no following 'D' "
2177 		    "at line %ld in file \"%s\""),
2178 		*line_num_p, fd->filename);
2179 	return -1;
2180     }
2181 
2182     /*
2183      * Get aperture no
2184      */
2185     ano = gerb_fgetint(fd, NULL);
2186 
2187     /*
2188      * Read in the whole aperture defintion and tokenize it
2189      */
2190     ad = gerb_fgetstring(fd, '*');
2191     token = strtok(ad, ",");
2192 
2193     if (token == NULL) {
2194 	gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
2195 		_("Invalid aperture definition "
2196 		    "at line %ld in file \"%s\""),
2197 		*line_num_p, fd->filename);
2198 	return -1;
2199     }
2200     if (strlen(token) == 1) {
2201 	switch (token[0]) {
2202 	case 'C':
2203 	    aperture->type = GERBV_APTYPE_CIRCLE;
2204 	    break;
2205 	case 'R' :
2206 	    aperture->type = GERBV_APTYPE_RECTANGLE;
2207 	    break;
2208 	case 'O' :
2209 	    aperture->type = GERBV_APTYPE_OVAL;
2210 	    break;
2211 	case 'P' :
2212 	    aperture->type = GERBV_APTYPE_POLYGON;
2213 	    break;
2214 	}
2215 	/* Here a should a T be defined, but I don't know what it represents */
2216     } else {
2217 	aperture->type = GERBV_APTYPE_MACRO;
2218 	/*
2219 	 * In aperture definition, point to the aperture macro
2220 	 * used in the defintion
2221 	 */
2222 	curr_amacro = amacro;
2223 	while (curr_amacro) {
2224 	    if ((strlen(curr_amacro->name) == strlen(token)) &&
2225 		(strcmp(curr_amacro->name, token) == 0)) {
2226 		aperture->amacro = curr_amacro;
2227 		break;
2228 	    }
2229 	    curr_amacro = curr_amacro->next;
2230 	}
2231     }
2232 
2233     /*
2234      * Parse all parameters
2235      */
2236     for (token = strtok(NULL, "X"), i = 0; token != NULL;
2237 	 token = strtok(NULL, "X"), i++) {
2238 	if (i == APERTURE_PARAMETERS_MAX) {
2239 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_ERROR, -1,
2240 		    _("Maximum number of allowed parameters exceeded "
2241 			"in aperture %d at line %ld in file \"%s\""),
2242 		    ano, *line_num_p, fd->filename);
2243 	    break;
2244 	}
2245 	errno = 0;
2246 
2247 	tempHolder = strtod(token, NULL);
2248 	/* convert any MM values to inches */
2249 	/* don't scale polygon angles or side numbers, or macro parmaeters */
2250 	if (!(((aperture->type == GERBV_APTYPE_POLYGON) && ((i==1) || (i==2)))||
2251 		(aperture->type == GERBV_APTYPE_MACRO))) {
2252 	    tempHolder /= scale;
2253 	}
2254 
2255 	aperture->parameter[i] = tempHolder;
2256 	if (errno) {
2257 	    gerbv_stats_printf(error_list, GERBV_MESSAGE_WARNING, -1,
2258 		    _("Failed to read all parameters exceeded in "
2259 			"aperture %d at line %ld in file \"%s\""),
2260 		    ano, *line_num_p, fd->filename);
2261             aperture->parameter[i] = 0.0;
2262         }
2263     }
2264 
2265     aperture->nuf_parameters = i;
2266 
2267     gerb_ungetc(fd);
2268 
2269     if (aperture->type == GERBV_APTYPE_MACRO) {
2270 	dprintf("Simplifying aperture %d using aperture macro \"%s\"\n", ano,
2271 		aperture->amacro->name);
2272 	simplify_aperture_macro(aperture, scale);
2273 	dprintf("Done simplifying\n");
2274     }
2275 
2276     g_free(ad);
2277 
2278     return ano;
2279 } /* parse_aperture_definition */
2280 
2281 
2282 /* ------------------------------------------------------------------ */
2283 static void
calc_cirseg_sq(struct gerbv_net * net,int cw,double delta_cp_x,double delta_cp_y)2284 calc_cirseg_sq(struct gerbv_net *net, int cw,
2285 	       double delta_cp_x, double delta_cp_y)
2286 {
2287     double d1x, d1y, d2x, d2y;
2288     double alfa, beta;
2289     int quadrant = 0;
2290 
2291 
2292     /*
2293      * Quadrant detection (based on ccw, converted below if cw)
2294      *  Y ^
2295      *   /!\
2296      *    !
2297      *    ---->X
2298      */
2299     if (net->start_x > net->stop_x)
2300 	/* 1st and 2nd quadrant */
2301 	if (net->start_y < net->stop_y)
2302 	    quadrant = 1;
2303 	else
2304 	    quadrant = 2;
2305     else
2306 	/* 3rd and 4th quadrant */
2307 	if (net->start_y > net->stop_y)
2308 	    quadrant = 3;
2309 	else
2310 	    quadrant = 4;
2311 
2312     /*
2313      * If clockwise, rotate quadrant
2314      */
2315     if (cw) {
2316 	switch (quadrant) {
2317 	case 1 :
2318 	    quadrant = 3;
2319 	    break;
2320 	case 2 :
2321 	    quadrant = 4;
2322 	    break;
2323 	case 3 :
2324 	    quadrant = 1;
2325 	    break;
2326 	case 4 :
2327 	    quadrant = 2;
2328 	    break;
2329 	default :
2330 	    GERB_COMPILE_ERROR(_("Unknow quadrant value while converting to cw"));
2331 	}
2332     }
2333 
2334     /*
2335      * Calculate arc center point
2336      */
2337     switch (quadrant) {
2338     case 1 :
2339 	net->cirseg->cp_x = net->start_x - delta_cp_x;
2340 	net->cirseg->cp_y = net->start_y - delta_cp_y;
2341 	break;
2342     case 2 :
2343 	net->cirseg->cp_x = net->start_x + delta_cp_x;
2344 	net->cirseg->cp_y = net->start_y - delta_cp_y;
2345 	break;
2346     case 3 :
2347 	net->cirseg->cp_x = net->start_x + delta_cp_x;
2348 	net->cirseg->cp_y = net->start_y + delta_cp_y;
2349 	break;
2350     case 4 :
2351 	net->cirseg->cp_x = net->start_x - delta_cp_x;
2352 	net->cirseg->cp_y = net->start_y + delta_cp_y;
2353 	break;
2354     default :
2355 	GERB_COMPILE_ERROR(_("Strange quadrant: %d"), quadrant);
2356     }
2357 
2358     /*
2359      * Some good values
2360      */
2361     d1x = fabs(net->start_x - net->cirseg->cp_x);
2362     d1y = fabs(net->start_y - net->cirseg->cp_y);
2363     d2x = fabs(net->stop_x - net->cirseg->cp_x);
2364     d2y = fabs(net->stop_y - net->cirseg->cp_y);
2365 
2366     alfa = atan2(d1y, d1x);
2367     beta = atan2(d2y, d2x);
2368 
2369     /*
2370      * Avoid divide by zero when sin(0) = 0 and cos(90) = 0
2371      */
2372     net->cirseg->width = alfa < beta ?
2373 	2 * (d1x / cos(alfa)) : 2 * (d2x / cos(beta));
2374     net->cirseg->height = alfa > beta ?
2375 	2 * (d1y / sin(alfa)) : 2 * (d2y / sin(beta));
2376 
2377     if (alfa < GERBV_PRECISION_ANGLE_RAD
2378     &&  beta < GERBV_PRECISION_ANGLE_RAD) {
2379 	net->cirseg->height = 0;
2380     }
2381 
2382     switch (quadrant) {
2383     case 1 :
2384 	net->cirseg->angle1 = RAD2DEG(alfa);
2385 	net->cirseg->angle2 = RAD2DEG(beta);
2386 	break;
2387     case 2 :
2388 	net->cirseg->angle1 = 180.0 - RAD2DEG(alfa);
2389 	net->cirseg->angle2 = 180.0 - RAD2DEG(beta);
2390 	break;
2391     case 3 :
2392 	net->cirseg->angle1 = 180.0 + RAD2DEG(alfa);
2393 	net->cirseg->angle2 = 180.0 + RAD2DEG(beta);
2394 	break;
2395     case 4 :
2396 	net->cirseg->angle1 = 360.0 - RAD2DEG(alfa);
2397 	net->cirseg->angle2 = 360.0 - RAD2DEG(beta);
2398 	break;
2399     default :
2400 	GERB_COMPILE_ERROR(_("Strange quadrant: %d"), quadrant);
2401     }
2402 
2403     if (net->cirseg->width < 0.0)
2404 	GERB_COMPILE_WARNING(_("Negative width [%f] in quadrant %d [%f][%f]"),
2405 			     net->cirseg->width, quadrant, RAD2DEG(alfa), RAD2DEG(beta));
2406 
2407     if (net->cirseg->height < 0.0)
2408 	GERB_COMPILE_WARNING(_("Negative height [%f] in quadrant %d [%f][%f]"),
2409 			     net->cirseg->height, quadrant, RAD2DEG(alfa), RAD2DEG(beta));
2410 
2411     return;
2412 
2413 } /* calc_cirseg_sq */
2414 
2415 
2416 /* Multiquadrant circular interpolation */
2417 static void
calc_cirseg_mq(struct gerbv_net * net,int cw,double delta_cp_x,double delta_cp_y)2418 calc_cirseg_mq(struct gerbv_net *net, int cw,
2419 	       double delta_cp_x, double delta_cp_y)
2420 {
2421     double d1x, d1y, d2x, d2y;
2422     double alfa, beta;
2423 
2424     net->cirseg->cp_x = net->start_x + delta_cp_x;
2425     net->cirseg->cp_y = net->start_y + delta_cp_y;
2426 
2427     /*
2428      * Some good values
2429      */
2430     d1x = -delta_cp_x;
2431     d1y = -delta_cp_y;
2432     d2x = net->stop_x - net->cirseg->cp_x;
2433     d2y = net->stop_y - net->cirseg->cp_y;
2434 
2435     /*
2436      * It's possible for values to be calculated that smaller than epsilon,
2437      * but containing opposite signs. These values cause essentially a
2438      * "signed zero" effect, which makes atan2 results unpredictable.
2439      */
2440     if (fabs(d1x) < DBL_EPSILON) d1x = 0;
2441     if (fabs(d1y) < DBL_EPSILON) d1y = 0;
2442     if (fabs(d2x) < DBL_EPSILON) d2x = 0;
2443     if (fabs(d2y) < DBL_EPSILON) d2y = 0;
2444 
2445     net->cirseg->width = hypot(delta_cp_x, delta_cp_y);
2446     net->cirseg->width *= 2.0;
2447     net->cirseg->height = net->cirseg->width;
2448 
2449     /*
2450      *  Keep values in radians, convert to degrees only results
2451      */
2452     alfa = atan2(d1y, d1x);
2453     beta = atan2(d2y, d2x);
2454 
2455     /*
2456      * Make sure it's always positive angles
2457      */
2458     if (alfa < 0.0) {
2459 	alfa += M_PI + M_PI;
2460 	beta += M_PI + M_PI;
2461     }
2462 
2463     if (beta < 0.0)
2464 	beta += M_PI + M_PI;
2465 
2466     /*
2467      * This is a sanity check for angles after the nature of atan2.
2468      * If cw we must make sure angle1-angle2 are always positive,
2469      * If ccw we must make sure angle2-angle1 are always negative.
2470      * We should really return one angle and the difference as GTK
2471      * uses them. But what the heck, it works for me.
2472      */
2473     if (cw) {
2474 	if (alfa - beta < DBL_EPSILON)
2475 	    beta -= M_PI + M_PI;
2476     } else {
2477 	if (beta - alfa < DBL_EPSILON)
2478 	    beta += M_PI + M_PI;
2479     }
2480 
2481     net->cirseg->angle1 = RAD2DEG(alfa);
2482     net->cirseg->angle2 = RAD2DEG(beta);
2483 }
2484 
2485 /* Calculate circular interpolation bounding box */
2486 static void
calc_cirseg_bbox(const gerbv_cirseg_t * cirseg,double apert_size_x,double apert_size_y,gerbv_render_size_t * bbox)2487 calc_cirseg_bbox(const gerbv_cirseg_t *cirseg,
2488 		double apert_size_x, double apert_size_y,
2489 		gerbv_render_size_t *bbox)
2490 {
2491 	gdouble x, y, ang1, ang2, step_pi_2;
2492 
2493 	/* For bounding box calculation only half of aperture size is used */
2494 	apert_size_x /= 2;
2495 	apert_size_y /= 2;
2496 
2497 	ang1 = DEG2RAD(MIN(cirseg->angle1, cirseg->angle2));
2498 	ang2 = DEG2RAD(MAX(cirseg->angle1, cirseg->angle2));
2499 
2500 	/* Start arc point */
2501 	x = cirseg->cp_x + cirseg->width*cos(ang1)/2;
2502 	y = cirseg->cp_y + cirseg->width*sin(ang1)/2;
2503 	gerber_update_min_and_max(bbox, x, y,
2504 				apert_size_x, apert_size_x,
2505 				apert_size_y, apert_size_y);
2506 
2507 	/* Middle arc points */
2508 	for (step_pi_2 = (ang1/M_PI_2 + 1)*M_PI_2;
2509 				step_pi_2 < MIN(ang2, ang1 + 2*M_PI);
2510 				step_pi_2 += M_PI_2) {
2511 		x = cirseg->cp_x + cirseg->width*cos(step_pi_2)/2;
2512 		y = cirseg->cp_y + cirseg->width*sin(step_pi_2)/2;
2513 		gerber_update_min_and_max(bbox, x, y,
2514 					apert_size_x, apert_size_x,
2515 					apert_size_y, apert_size_y);
2516 	}
2517 
2518 	/* Stop arc point */
2519 	x = cirseg->cp_x + cirseg->width*cos(ang2)/2;
2520 	y = cirseg->cp_y + cirseg->width*sin(ang2)/2;
2521 	gerber_update_min_and_max(bbox, x, y,
2522 				apert_size_x, apert_size_x,
2523 				apert_size_y, apert_size_y);
2524 }
2525 
2526 static void
gerber_update_any_running_knockout_measurements(gerbv_image_t * image)2527 gerber_update_any_running_knockout_measurements (gerbv_image_t *image)
2528 {
2529     if (knockoutMeasure) {
2530 	knockoutLayer->knockout.lowerLeftX = knockoutLimitXmin;
2531 	knockoutLayer->knockout.lowerLeftY = knockoutLimitYmin;
2532 	knockoutLayer->knockout.width = knockoutLimitXmax - knockoutLimitXmin;
2533 	knockoutLayer->knockout.height = knockoutLimitYmax - knockoutLimitYmin;
2534 	knockoutMeasure = FALSE;
2535     }
2536 }
2537 
2538 
2539 static void
gerber_calculate_final_justify_effects(gerbv_image_t * image)2540 gerber_calculate_final_justify_effects(gerbv_image_t *image)
2541 {
2542     gdouble translateA = 0.0, translateB = 0.0;
2543 
2544     if (image->info->imageJustifyTypeA != GERBV_JUSTIFY_NOJUSTIFY) {
2545 	if (image->info->imageJustifyTypeA == GERBV_JUSTIFY_CENTERJUSTIFY)
2546 	    translateA = (image->info->max_x - image->info->min_x) / 2.0;
2547 	else
2548 	    translateA = -image->info->min_x;
2549     }
2550     if (image->info->imageJustifyTypeB != GERBV_JUSTIFY_NOJUSTIFY) {
2551 	if (image->info->imageJustifyTypeB == GERBV_JUSTIFY_CENTERJUSTIFY)
2552 	    translateB = (image->info->max_y - image->info->min_y) / 2.0;
2553 	else
2554 	    translateB = -image->info->min_y;
2555     }
2556 
2557     /* update the min/max values so the autoscale function can correctly
2558        centered a justified image */
2559     image->info->min_x += translateA+ image->info->imageJustifyOffsetA;
2560     image->info->max_x += translateA+ image->info->imageJustifyOffsetA;
2561     image->info->min_y += translateB+ image->info->imageJustifyOffsetB;
2562     image->info->max_y += translateB+ image->info->imageJustifyOffsetB;
2563 
2564     /* store the absolute offset for the justify so we can quickly offset
2565        the rendered picture during drawing */
2566     image->info->imageJustifyOffsetActualA = translateA +
2567 	image->info->imageJustifyOffsetA;
2568     image->info->imageJustifyOffsetActualB = translateB +
2569 	image->info->imageJustifyOffsetB;
2570 } /* gerber_calculate_final_justify_effects */
2571 
2572 
2573 void
gerber_update_image_min_max(gerbv_render_size_t * boundingBox,double repeat_off_X,double repeat_off_Y,gerbv_image_t * image)2574 gerber_update_image_min_max (gerbv_render_size_t *boundingBox,
2575 		double repeat_off_X, double repeat_off_Y,
2576 		gerbv_image_t* image)
2577 {
2578 	image->info->min_x = MIN(image->info->min_x, boundingBox->left);
2579 	image->info->min_y = MIN(image->info->min_y, boundingBox->bottom);
2580 	image->info->max_x = MAX(image->info->max_x,
2581 			boundingBox->right + repeat_off_X);
2582 	image->info->max_y = MAX(image->info->max_y,
2583 			boundingBox->top + repeat_off_Y);
2584 }
2585 
2586 void
gerber_update_min_and_max(gerbv_render_size_t * boundingBox,gdouble x,gdouble y,gdouble apertureSizeX1,gdouble apertureSizeX2,gdouble apertureSizeY1,gdouble apertureSizeY2)2587 gerber_update_min_and_max(gerbv_render_size_t *boundingBox,
2588 			  gdouble x, gdouble y, gdouble apertureSizeX1,
2589 			  gdouble apertureSizeX2,gdouble apertureSizeY1,
2590 			  gdouble apertureSizeY2)
2591 {
2592     gdouble ourX1 = x - apertureSizeX1, ourY1 = y - apertureSizeY1;
2593     gdouble ourX2 = x + apertureSizeX2, ourY2 = y + apertureSizeY2;
2594 
2595     /* transform the point to the final rendered position, accounting
2596        for any scaling, offsets, mirroring, etc */
2597     /* NOTE: we need to already add/subtract in the aperture size since
2598        the final rendering may be scaled */
2599     cairo_matrix_transform_point (&currentMatrix, &ourX1, &ourY1);
2600     cairo_matrix_transform_point (&currentMatrix, &ourX2, &ourY2);
2601 
2602     /* check both points against the min/max, since depending on the rotation,
2603        mirroring, etc, either point could possibly be a min or max */
2604 
2605     boundingBox->left =   MIN(boundingBox->left,   ourX1);
2606     boundingBox->left =   MIN(boundingBox->left,   ourX2);
2607     boundingBox->right =  MAX(boundingBox->right,  ourX1);
2608     boundingBox->right =  MAX(boundingBox->right,  ourX2);
2609     boundingBox->bottom = MIN(boundingBox->bottom, ourY1);
2610     boundingBox->bottom = MIN(boundingBox->bottom, ourY2);
2611     boundingBox->top =    MAX(boundingBox->top,    ourY1);
2612     boundingBox->top =    MAX(boundingBox->top,    ourY2);
2613 } /* gerber_update_min_and_max */
2614 
2615 static gboolean
add_trailing_zeros_if_omitted(int * coord,int omitted_num,gerbv_format_t * format)2616 add_trailing_zeros_if_omitted(int *coord, int omitted_num,
2617 		gerbv_format_t *format)
2618 {
2619 	if (format
2620 	&&  format->omit_zeros == GERBV_OMIT_ZEROS_TRAILING
2621 	&&  omitted_num > 0) {
2622 		for (int i = 0; i < omitted_num; i++)
2623 			*coord *= 10;
2624 
2625 		return TRUE;
2626 	}
2627 
2628 	return FALSE;
2629 } /* add_trailing_zeros_if_omitted() */
2630 
2631 /** Return Gerber D-code name by code number. */
gerber_d_code_name(int d_code)2632 const char *gerber_d_code_name(int d_code)
2633 {
2634 	switch (d_code) {
2635 	case 1:  return N_("exposure on");
2636 	case 2:  return N_("exposure off");
2637 	case 3:  return N_("flash aperture");
2638 	default: return N_("unknown D-code");
2639 	}
2640 } /* gerber_d_code_name() */
2641 
2642 /** Return Gerber G-code name by code number. */
gerber_g_code_name(int g_code)2643 const char *gerber_g_code_name(int g_code)
2644 {
2645 	switch (g_code) {
2646 	case  0: return  N_("move");
2647 	case  1: return  N_("1X linear interpolation");
2648 	case  2: return  N_("CW interpolation");
2649 	case  3: return  N_("CCW interpolation");
2650 	case  4: return  N_("comment/ignore block");
2651 	case 10: return  N_("10X linear interpolation");
2652 	case 11: return  N_("0.1X linear interpolation");
2653 	case 12: return  N_("0.01X linear interpolation");
2654 	case 36: return  N_("poly fill on");
2655 	case 37: return  N_("poly fill off");
2656 	case 54: return  N_("tool prepare");
2657 	case 55: return  N_("flash prepare");
2658 	case 70: return  N_("units = inches");
2659 	case 71: return  N_("units = mm");
2660 	case 74: return  N_("disable 360 circ. interpolation");
2661 	case 75: return  N_("enable 360 circ. interpolation");
2662 	case 90: return  N_("absolute units");
2663 	case 91: return  N_("incremental units");
2664 	default: return  N_("unknown G-code");
2665 	}
2666 } /* gerber_g_code_name() */
2667 
2668 /** Return Gerber M-code name by code number. */
gerber_m_code_name(int m_code)2669 const char *gerber_m_code_name(int m_code)
2670 {
2671 	switch (m_code) {
2672 	case 0:  return N_("program stop (obsolete)");
2673 	case 1:  return N_("optional stop (obsolete)");
2674 	case 2:  return N_("end of file");
2675 	default: return N_("unknown M-code");
2676 	}
2677 } /* gerber_m_code_name() */
2678