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