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