--- /dev/null
+$ ./pcb2panel board1.pcb board2.pcb board3.pcb > boards.pcb
+$ pcb boards.pcb
+$ ./panel2pcb boards.pcb
+$ pcb boards.panel.pcb
--- /dev/null
+#!/usr/bin/perl
+# -*- perl -*-
+
+# Copyright 2006 DJ Delorie <dj@delorie.com>
+# Released under the terms of the GNU General Public License, version 2
+
+sub baseboard {
+ my ($file, $width, $height, $nbase) = @_;
+ if (! $nbase) {
+ $base = $file;
+ $base =~ s@.*/@@;
+ } else {
+ $base = $nbase;
+ }
+
+ $pscript = "$base.pscript";
+ open(PS, ">$pscript");
+ push(@files_to_remove, "$base.pscript");
+
+ open(S, $file) || die("$file: $!");
+ $outname = "$base.panel.pcb";
+ $outname =~ s/pnl\.panel\.pcb/pcb/;
+ open(O, ">$outname");
+ while (<S>) {
+ if (/PCB\[.* (\d+) (\d+)\]/) {
+ s/ (\d+) (\d+)\]/ $width $height\]/;
+ }
+ s/Cursor\[.*\]/Cursor[0 0 0.0]/;
+ next if /\b(Via|Pin|Pad|ElementLine|Line|Arc|ElementArc|Text)/;
+ if (/Polygon|Element/) {
+ while (<S>) {
+ last if /^\s*\)\s*$/;
+ }
+ next;
+ }
+ if (/Layer/) {
+ if (@panelvias) {
+ print O @panelvias;
+ @panelvias = ();
+ }
+ }
+ print O;
+ if (/Layer/) {
+ print O scalar <S>;
+ print O @panelcopper;
+ }
+ }
+ close O;
+ close S;
+
+ print PS "LoadFrom(Layout,$outname)\n";
+
+ $ox = $oy = 0;
+}
+
+sub loadboard {
+ my ($file) = @_;
+ $seq = 1 + $seq;
+
+ open(S, $file);
+ open(O, ">temp-panel.$seq");
+ while (<S>) {
+ if (/PCB\[.* (\d+) (\d+)\]/) {
+ $width = $1;
+ $height = $2;
+ }
+ s/Cursor\[.*\]/Cursor[0 0 0.0]/;
+ print O;
+ }
+ close O;
+ close S;
+ print PS "LoadFrom(LayoutToBuffer,temp-panel.$seq)\n";
+ push(@files_to_remove, "temp-panel.$seq");
+}
+
+sub opaste {
+ $vx = $ox;
+ $vy = $oy + $height;
+ print PS "PasteBuffer(ToLayout,$ox,$oy)\n";
+ $ox += $width;
+ $oy = 0;
+}
+
+sub vpaste {
+ print PS "PasteBuffer(ToLayout,$vx,$vy)\n";
+ $vy += $height;
+}
+
+sub done {
+ print PS "SaveTo(LayoutAs,$outname)\n";
+ print PS "Quit()\n";
+
+ close PS;
+
+ system "set -x; pcb --action-script $pscript";
+ #system "pcb -x ps $base.panel.pcb";
+ #unlink @files_to_remove;
+}
+
+1;
--- /dev/null
+#!/usr/bin/perl
+# -*- perl -*-
+
+# Copyright 2006 DJ Delorie <dj@delorie.com>
+# Released under the terms of the GNU General Public License, version 2
+
+if (! @ARGV) {
+ print "Usage: pcb2panel board1.pcb board2.pcb board3.pcb > boards.pcb";
+ print "Then edit boards.pcb, putting each outline where you want it\n";
+ print "and sizing the board. Then:\n";
+ print "panel2pcb boards.pcb\n";
+ print "and edit/print boards.panel.pcb\n";
+ exit 0;
+}
+
+$mydir = $0;
+if ($mydir =~ m@/@) {
+ $mydir =~ s@[^/]*$@@;
+} else {
+ $mydir = ".";
+}
+ require "$mydir/panel.pl";
+
+$panel = shift;
+
+open(P, $panel);
+while (<P>) {
+ if (/PCB\[.* (\d+) (\d+)\]/) {
+ $panel_width = $1;
+ $panel_height = $2;
+ }
+ if (/Element\[\"[^\"]*\" \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" (\d+) (\d+)/) {
+ $pcb = $1;
+ $base = $2;
+ $value = $3;
+ $mx = $4;
+ $my = $5;
+ %pinx = ();
+ %piny = ();
+ }
+ if (/Pin\[([\d-]+) ([\d-]+) \d+ \d+ \d+ \d+ \"(\d)\"/) {
+ $pinx{$3} = $1;
+ $piny{$3} = $2;
+ }
+ if ($pcb && /\)/) {
+ if ($pinx{'1'} < $pinx{'2'}) {
+ $rot = 0;
+ } elsif ($pinx{'1'} > $pinx{'2'}) {
+ $rot = 2;
+ } elsif ($piny{'1'} < $piny{'2'}) {
+ $rot = 3;
+ } elsif ($piny{'1'} > $piny{'2'}) {
+ $rot = 1;
+ }
+ push (@paste, "$pcb\0$rot\0$mx\0$my");
+ $pcb = undef;
+ }
+ if (/Via/) {
+ push (@panelvias, $_);
+ }
+ if (/^Layer\([^)]*\)$/) {
+ $junk = <P>; # The opening '('
+ while ($junk = <P>) {
+ last if $junk =~ /^\)/;
+ push (@panelcopper, $junk);
+ }
+ }
+}
+
+$tmp = "/tmp/panel$$.pcb";
+
+$start = $paste[0];
+$start =~ s/\0.*//;
+
+$panel =~ s/\.pcb$//;
+&baseboard($start, $panel_width, $panel_height, $panel);
+
+$lastboard = undef;
+for $paste (sort @paste) {
+ ($pcb, $rot, $mx, $my) = split(/\0/, $paste);
+ if ($lastboard ne $pcb) {
+ &loadboard ($pcb);
+ $lastboard = $pcb;
+ $lastrot = 0;
+ }
+ while ($lastrot != $rot) {
+ print PS "PasteBuffer(Rotate,1)\n";
+ $lastrot = ($lastrot+1) % 4;
+ }
+ print PS "PasteBuffer(ToLayout,$mx,$my)\n";
+}
+
+&done();
--- /dev/null
+#!/usr/bin/perl
+# -*- perl -*-
+
+# Copyright 2006 DJ Delorie <dj@delorie.com>
+# Released under the terms of the GNU General Public License, version 2
+
+if (! @ARGV) {
+ print "Usage: pcb2panel board1.pcb board2.pcb board3.pcb > boards.pcb";
+ print "Then edit boards.pcb, putting each outline where you want it\n";
+ print "and sizing the board. Then:\n";
+ print "panel2pcb boards.pcb\n";
+ print "and edit/print boards.panel.pcb\n";
+ exit 0;
+}
+
+for $pcb (@ARGV) {
+ $base = $pcb;
+ $base =~ s@.*/@@;
+ $base =~ s@\.pcb$@@;
+ $base{$pcb} = $base;
+ push (@pcbs, $pcb);
+ open(PCB, $pcb);
+ while (<PCB>) {
+ if (/^PCB\["" (\d+) (\d+)\]/) {
+ $width{$pcb} = $1;
+ $height{$pcb} = $2;
+ break;
+ }
+ }
+ close PCB;
+}
+
+$pw = 10000;
+$ph = 0;
+for $pcb (@pcbs) {
+ $pw += 10000;
+ $pw += $width{$pcb};
+ $ph = $height{$pcb} if $ph < $height{$pcb};
+}
+$ph += 20000;
+
+print "PCB[\"\" $pw $ph]\n";
+print "Grid[10000.0 0 0 1]\n";
+print "Groups(\"1,c:2,s\")\n";
+
+$x = 10000;
+$y = 10000;
+for $pcb (@pcbs) {
+ $desc = $pcb;
+ $name = $base{$pcb};
+ $value = "$width{$pcb} x $height{$pcb}";
+ $w = $width{$pcb};
+ $h = $height{$pcb};
+ print "Element[\"\" \"$desc\" \"$name\" \"$value\" $x $y 2000 2000 0 50 \"\"] (\n";
+ print " Pin[0 0 300 0 0 100 \"1\" \"1\" \"\"]\n";
+ print " Pin[$w 0 300 0 0 100 \"2\" \"2\" \"\"]\n";
+ print " ElementLine[0 0 $w 0 100]\n";
+ print " ElementLine[0 0 0 $h 100]\n";
+ print " ElementLine[$w 0 $w $h 100]\n";
+ print " ElementLine[0 $h $w $h 100]\n";
+ print ")\n";
+ $x += $w + 10000;
+}
+
+print "Layer(1 \"component\")()\n";
+print "Layer(2 \"solder\")()\n";
+print "Layer(3 \"silk\")()\n";
+print "Layer(4 \"silk\")()\n";
+
+exit 0;