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