2
* gEDA - GNU Electronic Design Automation
3
* This file is a part of gerbv.
5
* Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
23
/** @file pick-and-place.c
24
@contains the pick and place parser and renderer
29
#endif /* HAVE_CONFIG_H */
38
#include <gtk/gtk.h> /* What's this for? */
46
#endif /* HAVE_UNISTD_H */
53
#endif /* HAVE_GETOPT_H */
58
#include "gerb_error.h"
60
#ifdef RENDER_USING_GDK
69
#include "pick-and-place.h"
70
/* CHECKME - here gi18n is disabled */
71
#define _(String) (String)
74
#define max(a,b) ((a) > (b) ? (a) : (b))
76
#define min(a,b) ((a) < (b) ? (a) : (b))
78
/* DEBUG printing. #define DEBUG 1 in config.h to use this fcn. */
79
#define dprintf if(DEBUG) printf
81
//! Parses a string representing float number with a unit, default is mil
82
/** @param char a string to be screened for unit
83
@return a correctly converted double */
85
pick_and_place_get_float_unit(char *str)
90
/* float, optional space, optional unit mm,cm,in,mil */
91
sscanf(str, "%lf %40s", &x, unit);
92
if(strstr(unit,"in")) {
94
} else if(strstr(unit, "cm")) {
96
} else { /* default to mils */
101
} /* pick_and_place_get_float_unit*/
104
/** search a string for a delimiter.
105
Must occur at least n times. */
107
pick_and_place_screen_for_delimiter(char *str, int n)
110
char delimiter[4] = "|,;:";
112
int idx, idx_max = 0;
114
memset(counter, 0, sizeof(counter));
115
for(ptr = str; *ptr; ptr++) {
134
if(counter[idx] > counter[idx_max]) {
139
if (counter[idx_max] > n) {
140
return (unsigned char) delimiter[idx_max];
144
} /* pnp_screen_for_delimiter */
147
/**Parses the PNP data.
148
two lists are filled with the row data.\n One for the scrollable list in the search and select parts interface, the other one a mere two columned list, which drives the autocompletion when entering a search.\n
149
It also tries to determine the shape of a part and sets pnp_state->shape accordingly which will be used when drawing the selections as an overlay on screen.
150
@return the initial node of the pnp_state netlist
154
pick_and_place_parse_file(gerb_file_t *fd)
156
PnpPartData pnpPartData;
157
int lineCounter = 0, parsedLines = 0;
160
char buf[MAXL+2], buf0[MAXL+2];
162
gerb_transf_t *tr_rot = gerb_transf_new();
163
GArray *pnpParseDataArray = g_array_new (FALSE, FALSE, sizeof(PnpPartData));
164
gboolean foundValidDataRow = FALSE;
167
* many locales redefine "." as "," and so on, so sscanf has problems when
168
* reading Pick and Place files using %f format
170
setlocale(LC_NUMERIC, "C" );
172
while ( fgets(buf, MAXL, fd->fd) != NULL ) {
173
int len = strlen(buf)-1;
174
int i_length = 0, i_width = 0;
176
lineCounter += 1; /*next line*/
177
if(lineCounter < 2) {
179
* TODO in principle column names could be read and interpreted
180
* but we skip the first line with names of columns for this time
184
if(len >= 0 && buf[len] == '\n') {
187
if(len >= 0 && buf[len] == '\r') {
190
if (len <= 11) { //lets check a minimum length of 11
194
if ((len > 0) && (buf[0] == '%')) {
198
/* Abort if we see a G54 */
199
if ((len > 4) && (strncmp(buf,"G54 ", 4) == 0)) {
200
g_array_free (pnpParseDataArray, TRUE);
204
/* abort if we see a G04 code */
205
if ((len > 4) && (strncmp(buf,"G04 ", 4) == 0)) {
206
g_array_free (pnpParseDataArray, TRUE);
210
/* this accepts file both with and without quotes */
211
/* if (!pnp_state) { /\* we are in first line *\/ */
212
/* if ((delimiter = pnp_screen_for_delimiter(buf, 8)) < 0) { */
217
ret = csv_row_parse(buf, MAXL, buf0, MAXL, row, 11, ',', CSV_QUOTES);
220
foundValidDataRow = TRUE;
224
/* printf("direct:%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, ret %d\n", row[0], row[1], row[2],row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], ret); */
225
/* g_warning ("FFF %s %s\n",row[8],row[6]); */
227
if (row[0] && row[8]) { // here could be some better check for the syntax
228
snprintf (pnpPartData.designator, sizeof(pnpPartData.designator)-1, "%s", row[0]);
229
snprintf (pnpPartData.footprint, sizeof(pnpPartData.footprint)-1, "%s", row[1]);
230
snprintf (pnpPartData.layer, sizeof(pnpPartData.layer)-1, "%s", row[8]);
231
if (row[10] != NULL) {
232
if ( ! g_utf8_validate(row[10], -1, NULL)) {
233
gchar * str = g_convert(row[10], strlen(row[10]), "UTF-8", "ISO-8859-1",
235
// I have not decided yet whether it is better to use always
236
// "ISO-8859-1" or current locale.
237
// str = g_locale_to_utf8(row[10], -1, NULL, NULL, NULL);
238
snprintf (pnpPartData.comment, sizeof(pnpPartData.comment)-1, "%s", str);
241
snprintf (pnpPartData.comment, sizeof(pnpPartData.comment)-1, "%s", row[10]);
245
gchar* g_convert(const gchar *str, gssize len, const gchar *to_codeset, const gchar *from_codeset, gsize *bytes_read, gsize *bytes_written, GError **error);
247
pnpPartData.mid_x = pick_and_place_get_float_unit(row[2]);
248
pnpPartData.mid_y = pick_and_place_get_float_unit(row[3]);
249
pnpPartData.ref_x = pick_and_place_get_float_unit(row[4]);
250
pnpPartData.ref_y = pick_and_place_get_float_unit(row[5]);
251
pnpPartData.pad_x = pick_and_place_get_float_unit(row[6]);
252
pnpPartData.pad_y = pick_and_place_get_float_unit(row[7]);
253
/* This line causes segfault if we accidently starts parsing
254
* a gerber file. It is crap crap crap */
256
sscanf(row[9], "%lf", &pnpPartData.rotation); // no units, always deg
258
/* for now, default back to PCB program format
259
* TODO: implement better checking for format
261
else if (row[0] && row[1] && row[2] && row[3] && row[4] && row[5] && row[6]) {
262
snprintf (pnpPartData.designator, sizeof(pnpPartData.designator)-1, "%s", row[0]);
263
snprintf (pnpPartData.footprint, sizeof(pnpPartData.footprint)-1, "%s", row[1]);
264
snprintf (pnpPartData.layer, sizeof(pnpPartData.layer)-1, "%s", row[6]);
265
pnpPartData.mid_x = pick_and_place_get_float_unit(row[3]);
266
pnpPartData.mid_y = pick_and_place_get_float_unit(row[4]);
267
pnpPartData.pad_x = pnpPartData.mid_x + 0.03;
268
pnpPartData.pad_y = pnpPartData.mid_y + 0.03;
269
sscanf(row[5], "%lf", &pnpPartData.rotation); // no units, always deg
270
/* check for coordinate sanity, and abort if it fails
271
* Note: this is mainly to catch comment lines that get parsed
273
if ((fabs(pnpPartData.mid_x) < 0.001)&&(fabs(pnpPartData.mid_y) < 0.001)) {
282
* now, try and figure out the actual footprint shape to draw, or just
283
* guess something reasonable
285
if(sscanf(pnpPartData.footprint, "%02d%02d", &i_length, &i_width) == 2) {
286
// parse footprints like 0805 or 1206
287
pnpPartData.length = 0.01 * i_length;
288
pnpPartData.width = 0.01 * i_width;
289
pnpPartData.shape = PART_SHAPE_RECTANGLE;
291
gerb_transf_reset(tr_rot);
292
gerb_transf_rotate(tr_rot, -pnpPartData.rotation * M_PI/180);/* rotate it back to get dimensions */
293
gerb_transf_apply( pnpPartData.pad_x - pnpPartData.mid_x,
294
pnpPartData.pad_y - pnpPartData.mid_y, tr_rot, &tmp_x, &tmp_y);
295
if ((fabs(tmp_y) > fabs(tmp_x/100)) && (fabs(tmp_x) > fabs(tmp_y/100))){
296
pnpPartData.length = 2 * tmp_x;/* get dimensions*/
297
pnpPartData.width = 2 * tmp_y;
298
pnpPartData.shape = PART_SHAPE_STD;
300
pnpPartData.length = 0.015;
301
pnpPartData.width = 0.015;
302
pnpPartData.shape = PART_SHAPE_UNKNOWN;
305
g_array_append_val (pnpParseDataArray, pnpPartData);
308
gerb_transf_free(tr_rot);
310
/* rewind(fd->fd); */
312
/* so a sanity check and see if this is a valid pnp file */
313
if ((((float) parsedLines / (float) lineCounter) < 0.3) ||
314
(!foundValidDataRow)) {
315
/* this doesn't look like a valid PNP file, so return error */
316
g_array_free (pnpParseDataArray, TRUE);
319
return pnpParseDataArray;
320
} /* pick_and_place_parse_file */
323
/* ------------------------------------------------------------------
324
* pick_and_place_check_file_type
325
* ------------------------------------------------------------------
326
* Description: Tries to parse the given file into a pick-and-place
327
* data set. If it fails to read any good rows, then returns
328
* FALSE, otherwise it returns TRUE.
330
* ------------------------------------------------------------------
333
pick_and_place_check_file_type(gerb_file_t *fd, gboolean *returnFoundBinary)
339
gboolean found_binary = FALSE;
340
gboolean found_G54 = FALSE;
341
gboolean found_M0 = FALSE;
342
gboolean found_M2 = FALSE;
343
gboolean found_G2 = FALSE;
344
gboolean found_ADD = FALSE;
345
gboolean found_comma = FALSE;
346
gboolean found_R = FALSE;
347
gboolean found_U = FALSE;
348
gboolean found_C = FALSE;
349
gboolean found_boardside = FALSE;
353
GERB_FATAL_ERROR("malloc buf failed while checking for pick-place file.\n");
355
while (fgets(buf, MAXL, fd->fd) != NULL) {
358
/* First look through the file for indications of its type */
360
/* check for non-binary file */
361
for (i = 0; i < len; i++) {
362
if (!isprint((int) buf[i]) && (buf[i] != '\r') &&
363
(buf[i] != '\n') && (buf[i] != '\t')) {
368
if (g_strstr_len(buf, len, "G54")) {
371
if (g_strstr_len(buf, len, "M00")) {
374
if (g_strstr_len(buf, len, "M02")) {
377
if (g_strstr_len(buf, len, "G02")) {
380
if (g_strstr_len(buf, len, "ADD")) {
383
if (g_strstr_len(buf, len, ",")) {
386
/* Semicolon can be separator too */
387
if (g_strstr_len(buf, len, ";")) {
391
/* Look for refdes -- This is dumb, but what else can we do? */
392
if ((letter = g_strstr_len(buf, len, "R")) != NULL) {
393
if (isdigit((int) letter[1])) { /* grab char after R */
397
if ((letter = g_strstr_len(buf, len, "C")) != NULL) {
398
if (isdigit((int) letter[1])) { /* grab char after C */
402
if ((letter = g_strstr_len(buf, len, "U")) != NULL) {
403
if (isdigit((int) letter[1])) { /* grab char after U */
408
/* Look for board side indicator since this is required
410
if (g_strstr_len(buf, len, "top")) {
411
found_boardside = TRUE;
413
if (g_strstr_len(buf, len, "Top")) {
414
found_boardside = TRUE;
416
if (g_strstr_len(buf, len, "TOP")) {
417
found_boardside = TRUE;
419
/* Also look for evidence of "Layer" in header.... */
420
if (g_strstr_len(buf, len, "ayer")) {
421
found_boardside = TRUE;
423
if (g_strstr_len(buf, len, "AYER")) {
424
found_boardside = TRUE;
431
/* Now form logical expression determining if this is a pick-place file */
432
*returnFoundBinary = found_binary;
443
if (found_comma && (found_R || found_C || found_U) &&
449
} /* pick_and_place_check_file_type */
452
/* ------------------------------------------------------------------
453
* pick_and_place_convert_pnp_data_to_image
454
* ------------------------------------------------------------------
455
* Description: Render a parsedPickAndPlaceData array into a gerb_image.
457
* ------------------------------------------------------------------
460
pick_and_place_convert_pnp_data_to_image(GArray *parsedPickAndPlaceData, gint boardSide)
462
gerb_image_t *image = NULL;
463
gerb_net_t *curr_net = NULL;
465
gerb_transf_t *tr_rot = gerb_transf_new();
466
drill_stats_t *stats; /* Eventually replace with pick_place_stats */
467
gboolean foundElement = FALSE;
469
/* step through and make sure we have an element on the layer before
470
we actually create a new image for it and fill it */
471
for (i = 0; i < parsedPickAndPlaceData->len; i++) {
472
PnpPartData partData = g_array_index(parsedPickAndPlaceData, PnpPartData, i);
474
if ((boardSide == 0) && !((partData.layer[0]=='b') || (partData.layer[0]=='B')))
476
if ((boardSide == 1) && !((partData.layer[0]=='t') || (partData.layer[0]=='T')))
484
image = new_gerb_image(image);
486
GERB_FATAL_ERROR("malloc image failed\n");
489
image->format = (gerb_format_t *)g_malloc(sizeof(gerb_format_t));
490
if (image->format == NULL) {
491
GERB_FATAL_ERROR("malloc format failed\n");
493
memset((void *)image->format, 0, sizeof(gerb_format_t));
495
image->layertype = PICK_AND_PLACE;
496
stats = drill_stats_new();
498
GERB_FATAL_ERROR("malloc pick_place_stats failed\n");
499
image->drill_stats = stats;
502
curr_net = image->netlist;
503
curr_net->layer = image->layers;
504
curr_net->state = image->states;
505
image->info->min_x = HUGE_VAL;
506
image->info->min_y = HUGE_VAL;
507
image->info->max_x = -HUGE_VAL;
508
image->info->max_y = -HUGE_VAL;
510
image->aperture[0] = (gerb_aperture_t *)g_malloc(sizeof(gerb_aperture_t));
511
memset((void *) image->aperture[0], 0, sizeof(gerb_aperture_t));
512
image->aperture[0]->type = CIRCLE;
513
image->aperture[0]->amacro = NULL;
514
image->aperture[0]->parameter[0] = 0.02;
515
image->aperture[0]->nuf_parameters = 1;
517
for (i = 0; i < parsedPickAndPlaceData->len; i++) {
518
PnpPartData partData = g_array_index(parsedPickAndPlaceData, PnpPartData, i);
520
curr_net->next = (gerb_net_t *)g_malloc(sizeof(gerb_net_t));
521
curr_net = curr_net->next;
523
memset((void *)curr_net, 0, sizeof(gerb_net_t));
524
curr_net->layer = image->layers;
525
curr_net->state = image->states;
526
partData.rotation *= M_PI/180; /* convert deg to rad */
527
/* check if the entry is on the specified layer */
528
if ((boardSide == 0) && !((partData.layer[0]=='b') || (partData.layer[0]=='B')))
530
if ((boardSide == 1) && !((partData.layer[0]=='t') || (partData.layer[0]=='T')))
532
if ((partData.shape == PART_SHAPE_RECTANGLE) ||
533
(partData.shape == PART_SHAPE_STD)) {
534
// TODO: draw rectangle length x width taking into account rotation or pad x,y
535
gerb_transf_reset(tr_rot);
537
gerb_transf_shift(tr_rot, partData.mid_x, partData.mid_y);
538
/* unrotate the part to make sure the label is in the same location */
539
gerb_transf_rotate(tr_rot, -partData.rotation);
540
/* this first net is just a label holder, so calculate the lower
541
left location to line up above the element */
542
gerb_transf_apply(-partData.length/2,partData.width/2, tr_rot,
543
&curr_net->start_x, &curr_net->start_y);
544
gerb_transf_apply(-partData.length/2,partData.width/2, tr_rot,
545
&curr_net->stop_x, &curr_net->stop_y);
546
/* re-rotate back to the correct orientation */
547
gerb_transf_rotate(tr_rot, partData.rotation);
549
curr_net->aperture = 0;
550
curr_net->aperture_state = OFF;
551
curr_net->interpolation = LINEARx1;
552
curr_net->layer = image->layers;
553
curr_net->state = image->states;
555
/* assign a label to this first draw primitive, in case we want
556
* to render some text next to the mark
558
if (strlen (partData.designator) > 0) {
559
curr_net->label = g_string_new (partData.designator);
562
/* rotate 180 to line up with PCB standard notation */
563
gerb_transf_rotate(tr_rot, M_PI);
565
curr_net->next = (gerb_net_t *)g_malloc(sizeof(gerb_net_t));
566
curr_net = curr_net->next;
568
memset((void *)curr_net, 0, sizeof(gerb_net_t));
569
gerb_transf_apply(partData.length/2, partData.width/2, tr_rot,
570
&curr_net->start_x, &curr_net->start_y);
571
gerb_transf_apply(-partData.length/2, partData.width/2, tr_rot,
572
&curr_net->stop_x, &curr_net->stop_y);
574
curr_net->aperture = 0;
575
curr_net->aperture_state = ON;
576
curr_net->interpolation = LINEARx1;
577
curr_net->layer = image->layers;
578
curr_net->state = image->states;
581
curr_net->next = (gerb_net_t *)g_malloc(sizeof(gerb_net_t));
582
curr_net = curr_net->next;
584
memset((void *)curr_net, 0, sizeof(gerb_net_t));
586
gerb_transf_apply(-partData.length/2, partData.width/2, tr_rot,
587
&curr_net->start_x, &curr_net->start_y);
588
gerb_transf_apply(-partData.length/2, -partData.width/2, tr_rot,
589
&curr_net->stop_x, &curr_net->stop_y);
591
curr_net->aperture = 0;
592
curr_net->aperture_state = ON;
593
curr_net->interpolation = LINEARx1;
594
curr_net->layer = image->layers;
595
curr_net->state = image->states;
597
curr_net->next = (gerb_net_t *)g_malloc(sizeof(gerb_net_t));
598
curr_net = curr_net->next;
600
memset((void *)curr_net, 0, sizeof(gerb_net_t));
602
gerb_transf_apply(-partData.length/2, -partData.width/2, tr_rot,
603
&curr_net->start_x, &curr_net->start_y);
604
gerb_transf_apply(partData.length/2, -partData.width/2, tr_rot,
605
&curr_net->stop_x, &curr_net->stop_y);
607
curr_net->aperture = 0;
608
curr_net->aperture_state = ON;
609
curr_net->interpolation = LINEARx1;
610
curr_net->layer = image->layers;
611
curr_net->state = image->states;
613
curr_net->next = (gerb_net_t *)g_malloc(sizeof(gerb_net_t));
614
curr_net = curr_net->next;
616
memset((void *)curr_net, 0, sizeof(gerb_net_t));
618
gerb_transf_apply(partData.length/2, -partData.width/2, tr_rot,
619
&curr_net->start_x, &curr_net->start_y);
620
gerb_transf_apply(partData.length/2, partData.width/2, tr_rot,
621
&curr_net->stop_x, &curr_net->stop_y);
623
curr_net->aperture = 0;
624
curr_net->aperture_state = ON;
625
curr_net->interpolation = LINEARx1;
626
curr_net->layer = image->layers;
627
curr_net->state = image->states;
629
curr_net->next = (gerb_net_t *)g_malloc(sizeof(gerb_net_t));
630
curr_net = curr_net->next;
632
memset((void *)curr_net, 0, sizeof(gerb_net_t));
634
if (partData.shape == PART_SHAPE_RECTANGLE) {
635
gerb_transf_apply(partData.length/4, -partData.width/2, tr_rot,
636
&curr_net->start_x, &curr_net->start_y);
637
gerb_transf_apply(partData.length/4, partData.width/2, tr_rot,
638
&curr_net->stop_x, &curr_net->stop_y);
640
gerb_transf_apply(partData.length/4, partData.width/2, tr_rot,
641
&curr_net->start_x, &curr_net->start_y);
642
gerb_transf_apply(partData.length/4, partData.width/4, tr_rot,
643
&curr_net->stop_x, &curr_net->stop_y);
645
curr_net->aperture = 0;
646
curr_net->aperture_state = ON;
647
curr_net->interpolation = LINEARx1;
648
curr_net->layer = image->layers;
649
curr_net->state = image->states;
651
curr_net->next = (gerb_net_t *)g_malloc(sizeof(gerb_net_t));
652
curr_net = curr_net->next;
654
memset((void *)curr_net, 0, sizeof(gerb_net_t));
655
gerb_transf_apply(partData.length/2, partData.width/4, tr_rot,
656
&curr_net->start_x, &curr_net->start_y);
657
gerb_transf_apply(partData.length/4, partData.width/4, tr_rot,
658
&curr_net->stop_x, &curr_net->stop_y);
660
curr_net->aperture = 0;
661
curr_net->aperture_state = ON;
662
curr_net->interpolation = LINEARx1;
663
curr_net->layer = image->layers;
664
curr_net->state = image->states;
665
/* calculate a rough radius for the min/max screen calcs later */
666
radius = max (partData.length/2, partData.width/2);
668
curr_net->start_x = partData.mid_x;
669
curr_net->start_y = partData.mid_y;
670
curr_net->stop_x = partData.pad_x;
671
curr_net->stop_y = partData.pad_y;
673
curr_net->aperture = 0;
674
curr_net->aperture_state = ON;
675
curr_net->interpolation = LINEARx1;
676
curr_net->layer = image->layers;
677
curr_net->state = image->states;
679
curr_net->next = (gerb_net_t *)g_malloc(sizeof(gerb_net_t));
680
curr_net = curr_net->next;
682
memset((void *)curr_net, 0, sizeof(gerb_net_t));
684
curr_net->start_x = partData.mid_x;
685
curr_net->start_y = partData.mid_y;
686
curr_net->stop_x = partData.pad_x;
687
curr_net->stop_y = partData.pad_y;
689
curr_net->aperture = 0;
690
curr_net->aperture_state = ON;
691
curr_net->interpolation = CW_CIRCULAR;
692
curr_net->layer = image->layers;
693
curr_net->state = image->states;
695
curr_net->cirseg = (gerb_cirseg_t *)g_malloc(sizeof(gerb_cirseg_t));
696
memset((void *)curr_net->cirseg, 0, sizeof(gerb_cirseg_t));
697
curr_net->cirseg->angle1 = 0.0;
698
curr_net->cirseg->angle2 = 360.0;
699
curr_net->cirseg->cp_x = partData.mid_x;
700
curr_net->cirseg->cp_y = partData.mid_y;
701
radius = sqrt((partData.pad_x-partData.mid_x)*(partData.pad_x-partData.mid_x) +
702
(partData.pad_y-partData.mid_y)*(partData.pad_y-partData.mid_y));
705
curr_net->cirseg->width = 2*radius; /* fabs(pad_x-mid_x) */
706
curr_net->cirseg->height = 2*radius;
710
* update min and max numbers so the screen zoom-to-fit
713
image->info->min_x = min(image->info->min_x, (partData.mid_x - radius - 0.02));
714
image->info->min_y = min(image->info->min_y, (partData.mid_y - radius - 0.02));
715
image->info->max_x = max(image->info->max_x, (partData.mid_x + radius + 0.02));
716
image->info->max_y = max(image->info->max_y, (partData.mid_y + radius + 0.02));
718
curr_net->next = NULL;
720
gerb_transf_free(tr_rot);
722
} /* pick_and_place_parse_file_to_image */
725
/* ------------------------------------------------------------------
726
* pick_and_place_parse_file_to_image
727
* ------------------------------------------------------------------
728
* Description: Renders a pick and place file to a gerb_image.
729
* Notes: The file format should already be verified before calling
730
* this function, since it does very little sanity checking itself.
731
* ------------------------------------------------------------------
734
pick_and_place_parse_file_to_images(gerb_file_t *fd, gerb_image_t **topImage,
735
gerb_image_t **bottomImage)
737
GArray *parsedPickAndPlaceData = pick_and_place_parse_file (fd);
739
*bottomImage = pick_and_place_convert_pnp_data_to_image(parsedPickAndPlaceData, 0);
740
*topImage = pick_and_place_convert_pnp_data_to_image(parsedPickAndPlaceData, 1);
742
g_array_free (parsedPickAndPlaceData, TRUE);
743
} /* pick_and_place_parse_file_to_image */