#! /usr/bin/perl

#
# initialize variables
#
$date = `date`;
$date =~ s/\n$//;
$batchmode = 0;
$outputstyle = 'ps';
$reportto = 'crosspad@itojun.org';
$debug = 0;
($startpage, $endpage) = (1, 99999);
$pageoutput = 0;
$pspagenumber = 0;
($xoffset, $yoffset) = (0, 0);
($xscale, $yscale) = (1, 1);
$tmpdir = '/tmp/' . $$ . '.' . time;
($boundingx0, $boundingy0) = (99999, 99999);
($boundingx1, $boundingy1) = (-99999, -99999);
# western people may prefer letter than a4...
$papersize = 'a4 letter';
#$papersize = 'letter a4';

$pslinewidth = 0;
$annotate = 1;
$reorder = 1;
$purge = 1;
$tgifscale = 0.5;
$idrawscale = 0.5;

$progname = $0;
$progname =~ s/\.pl$//;
$progname =~ s/^.*\///;
$outputstyle = $1 if ($progname =~ /^crosspad2(\w+)$/);

$huffman{'110101011111001'} = -16;
$huffman{'110101011110'} = -15;
$huffman{'1100100100'} = -14;
$huffman{'110010011'} = -13;
$huffman{'110101010'} = -12;
$huffman{'11010100'} = -11;
$huffman{'1000100'} = -10;
$huffman{'1010011'} = -9;
$huffman{'1101011'} = -8;
$huffman{'101000'} = -7;
$huffman{'110011'} = -6;
$huffman{'10000'} = -5;
$huffman{'11000'} = -4;
$huffman{'11011'} = -3;
$huffman{'1011'} = -2;
$huffman{'010'} = -1;
$huffman{'00'} = 0;
$huffman{'011'} = 1;
$huffman{'1001'} = 2;
$huffman{'10101'} = 3;
$huffman{'110100'} = 4;
$huffman{'100011'} = 5;
$huffman{'1100101'} = 6;
$huffman{'1010010'} = 7;
$huffman{'11001000'} = 8;
$huffman{'10001010'} = 9;
$huffman{'100010111'} = 10;
$huffman{'100010110'} = 11;
$huffman{'1100100101'} = 12;
$huffman{'11010101110'} = 13;
$huffman{'11010101100'} = 14;
$huffman{'11010101101'} = 15;
$huffman{'1101010111111'} = 16;
$huffman{'11010101111101'} = 18;

# long form covers signed 8bit values
# slow version...
#for ($i = -128; $i < 128; $i++) {
#	$t = unpack('B*', pack('C', $i));
#	$huffman{'110101011111000' . $t} = $i + 5;
#	print '110101011111000' . $t . "=";
#	print $i + 5 . "\n";
#}
$huffman{'110101011111000'} = -998;
# termination
$huffman{'1111111'} = -999;
#$huffman{'111111111111111'} = -999;

#
# parse arguments
#
ARG: while ($ARGV[0] =~ /^-/) {
	$_ = shift @ARGV;
	if ($_ =~ /-s(\d+)/) { $startpage = $1; next ARG; }
	if ($_ =~ /-e(\d+)/) { $endpage = $1; next ARG; }
	if ($_ =~ /-p(\d+)/) { $startpage = $endpage = $1; next ARG; }
	if ($_ =~ /-m(\w+)/) { $outputstyle = $1; next ARG; }
	if ($_ =~ /-w(\d+)/) { $pslinewidth = $1; next ARG; }
	$t = 0;
	foreach $_ (split(//, substr($_, 1))) {
		if ($_ eq 'i') { $t++; $batchmode = 0; next; }
		if ($_ eq 'b') { $t++; $batchmode = 1; next; }
		if ($_ eq 'D') { $t++; $debug++; next; }
		if ($_ eq 'r') { $t++; $reorder = !$reorder; next; }
		if ($_ eq 'a') { $t++; $annotate = !$annotate; next; }
		&usage; exit 1;
	}
	next ARG if ($t);
	&usage; exit 1;
}
if (scalar(@ARGV) != 1) {
	&usage; exit 1;
}

if ($outputstyle eq 'eps') {
	$endpage = $startpage;
} elsif ($outputstyle eq 'tgif' || $outputstyle eq 'idraw') {
	$endpage = $startpage;
	$reorder = 1;
}

$filename = $ARGV[0];
if (! -f $filename) {
	print STDERR "$filename not found\n"; exit 1;
}
if ($reorder) {
	while (-e $tmpdir) {
		$tmpdir .= 'x';
	}
	mkdir($tmpdir, 0755) || die;
}
&pageinit(0);
if (!$reorder) {
	&preprocess($filename);
	&preamble0;
}
&parse($filename);
if (!$reorder) {
	&postamble0;
	&postprocess($filename);
} else {
	&joinpages;
	system "/bin/rm -fr $tmpdir" if ($purge);
}
exit 0;

sub pageinit {
	local($page) = @_;
	local($t);

	return if (!$reorder);

	$curpage = $page;
	($boundingx0, $boundingy0) = (99999, 99999);
	($boundingx1, $boundingy1) = (-99999, -99999);
	if (open(BOUND, "< $tmpdir/$page.bound")) {
		$t = <BOUND>;
		close(BOUND);
		($boundingx0, $boundingy0, $boundingx1, $boundingy1)
			= split(/\s+/, $t);
	}
	open(OUT, ">> $tmpdir/$page") || die;
	select(OUT);
}

sub pagefinish {
	return if (!$reorder);

	close(OUT);
	open(OUT, "> $tmpdir/$curpage.bound") || die;
	print OUT "$boundingx0 $boundingy0 $boundingx1 $boundingy1\n";
	close(OUT);
}

sub parse {
	local($file) = @_;
	local($buf, $l, $sl, $nl, $x, $y, $len, $vers, $code);
	local(@unknownlen);

	for ($i = 0; $i < 256; $i++) {
		$unknownlen[$i] = -1;
		$unknownstr[$i] = '';
	}
	$unknownlen[0x03] = 0x0b; $unknownstr[0x03] = 'keyword';
	$unknownlen[0x0e] = 0x07;
	$unknownlen[0x35] = 0x06;
	$unknownlen[0x36] = 0x06;
	$unknownlen[0x39] = 0x27;
	$unknownlen[0x3c] = 0x12;
	$unknownlen[0x3d] = 0x10;
	$unknownlen[0x3e] = 0x0a;

	open(IN, "< $file") || die;

	sysread(IN, $buf, 5) || die;
	if (substr($buf, 0, 3) ne pack('C3', 0x01, 0x00, 0xff)) {
		close(IN);
		open(IN, "< $file") || die;
		$len = (stat($file))[7];
	} else {
		# just for debugging...
		$len = (stat($file))[7] - 5;
	}
	print "% file \"$file\" data part length: $len\n" if ($debug);

	$l = 0;
	while ($l + 5 < $len) {
		sysread(IN, $buf, 5);
		($vers, $code) = unpack('N C', $buf);	# correct?
		printf("%% pos=%08x ver=%d code=0x%02x: ", $l, $vers, $code)
			if ($debug);
		if ($vers != 0) {
			print STDERR "unsupported file format version $vers\n";
			close(IN);
			&failure("unexpected input", 0);
		}

		if ($code == 0x06) {
			# original crosspad
			sysread(IN, $buf, $nl = 0x14);
			$t = unpack('x19 C', $buf);
			if ($debug) {
				&printdate(substr($buf, 0, 6));
				print "compress=$t\n";
			}

			# is this correct?
			if ($t == 1) {
				($xoffset, $yoffset) = (20, 22);
				($xscale, $yscale) = (1, 1);
			} elsif ($t == 2) {
				($xoffset, $yoffset) = (20, 2944);
				($xscale, $yscale) = (1, -1);
			}
		} elsif ($code == 0x1d) {
			# crosspad XP
			sysread(IN, $buf, $nl = 0x2b);
			if ($debug) {
				&printdate(substr($buf, 0, 6));
				print "compress=?\n";
			}

			# is this correct?
			# handcrafted offset value, need suggestions
			($xoffset, $yoffset) = (40, 3030);
			($xscale, $yscale) = (1, -1);
			$papersize = 'a5';
		} elsif ($code == 0x0d || $code == 0x0a) {
			sysread(IN, $buf, $nl = 0x0e);
			$t = substr($buf, 6, 8);
			$t =~ s/\000//g;
			if ($debug) {
				&printdate(substr($buf, 0, 6));
				print "\"$t\"\n";
			}
		} elsif ($code == 0x04) {
			sysread(IN, $buf, $nl = 10);
			$t = unpack('x6 N', $buf);
			if ($debug) {
				&printdate(substr($buf, 0, 6));
				print "switch to " if ($reorder);
				print "page $t\n";
			}

			if ($reorder) {
				&pagefinish;
			} else {
				&postamble if ($pageoutput);
			}

			if ($reorder) {
				&pageinit($t);
				$pageoutput = 1;
			} else {
				if ($startpage <= $t && $t <= $endpage) {
					$pageoutput = 1;
					&preamble(++$pspagenumber);
					print "% this is page $t ".
						"in the source file\n"
						if ($pspagenumber != $t);
				} else {
					$pageoutput = 0;
					print "% skipping this page\n"
						if ($debug);
				}
			}
		} elsif ($code == 0x01 || $code == 0x02) {
			sysread(IN, $buf, 12);
			($sl, $x, $y) = unpack('x6 n3', $buf);
			if ($debug) {
				&printdate(substr($buf, 0, 6));
				if ($code == 0x01) {
					print "stroke: ";
				} else {
					print "NEW stroke: ";
				}
				print "len=$sl start=($x, $y)\n";
			}

			sysread(IN, $buf, $sl - 4);
			if ($pageoutput) {
				($curx, $cury) = ($x - $xoffset, $y - $yoffset);
				$curx *= $xscale;
				$cury *= $yscale;
				&updatebbox($curx, $cury);
				if ($outputstyle =~ /^(ps|eps)$/) {
					printf("%d %d NP [\n", $curx, $cury);
				} elsif ($outputstyle =~ /^(tgif|idraw)$/) {
					printf("START %d %d\n", $curx, $cury);
				}
				&printstroke($buf);
			}

			$nl = 12 + length($buf);
		} elsif ($code == 0x05) {
			# clock adjust
			sysread(IN, $buf, $nl = 0x0c);
			if ($debug) {
				&printdate(substr($buf, 0, 6));
				print "clock adjusted at ";
				&printdate(substr($buf, 6, 6));
				print "\n";
			}
		} elsif ($code == 0x37) {
			sysread(IN, $buf, $nl = 6);
			if ($debug) {
				&printdate(substr($buf, 0, 6));
				print "page bookmark\n";
			}
			if ($reorder) {
				open(BOOKMARK, "> $tmpdir/$curpage.bookmark");
				$t = select(BOOKMARK);
				&printdate(substr($buf, 0, 6));
				select($t);
				close(BOOKMARK);
			}
		} elsif ($code == 0x3a) {
			sysread(IN, $buf, $nl = 6);
			if ($debug) {
				&printdate(substr($buf, 0, 6));
				print "is last download time\n";
			}
		} elsif (0 <= $unknownlen[$code]) {
			sysread(IN, $buf, $nl = $unknownlen[$code]);
			if ($debug == 1) {
				&printdate(substr($buf, 0, 6));
				print $unknownstr[$code] . "???\n";
			} elsif (1 < $debug) {
				&printdate(substr($buf, 0, 6));
				print $unknownstr[$code] . "???: ";
				for ($i = 6; $i < length($buf); $i++) {
					printf("%02x ", unpack('C',
						substr($buf, $i, 1)));
				}
				print "\n";
			}
		} else {
			printf("unknown code %02x\n", $code);
			die;
		}
		$l += 5;
		$l += $nl;
	}
	if ($reorder) {
		&pagefinish;
	} else {
		&postamble if ($pageoutput);
	}
#	if ($l != $len) {
#		print "short file: l=$l len=$len\n";
#	}
	close(IN);
}

sub printdate {
	local($datebcd) = @_;
	local(@t);

	@t = unpack('C6', $datebcd);
	printf("%02x/%02x/%02x %02x:%02x:%02x  ",
		$t[0], $t[1], $t[2], $t[3], $t[4], $t[5]);
}

sub printstroke {
	local($stroke) = @_;
	local($r, $trailer, $v);
	local(@t);
	local($i);
	local($bits, $maxbits);
	local($cnt);
	local($npoints0, $npoints);

	$maxbits = -1;
	$r = '';
	$trailer = substr($stroke, index($stroke, pack('C2', 0xff, 0xff)));
	$npoints0 = $npoints = unpack('x2 n', $trailer);
	print "% npoints=$npoints0\n" if ($debug);
	if ($outputstyle =~ /^(tgif|idraw)$/) {
		printf("NPOINTS %d\n", $npoints0);
	}

	$r = unpack('B*', $stroke);
	$r =~ s/\s+//g;
	@t = sort {length($b) <=> length($a)} (keys %huffman);
	$cnt = 0;
	$npoints *= 2;
	while (2 < $npoints && length($r)) {
		foreach $i (@t) {
			if (substr($r, 0, length($i)) eq $i) {
				$m = $i;
				last;
			}
		}

		if ($huffman{$m} == -999) {
			print "% huffman code length mismatch? "
				. "seen temrination. npoints=$npoints0\n"
				if ($debug);
			last;
		} elsif ($huffman{$m} == -998) {
			$v = unpack('c', pack('B*', substr($r, length($m), 8)));
			$m .= substr($r, length($m), 8);
		} elsif (length($m) && substr($r, 0, length($m)) eq $m) {
			$v = $huffman{$m};
		} else {
			&failure("unknown huffman code ". substr($r, 0, 10), 1);
				
			print STDERR "please contact $reportto so that "
				. "the tool can be more perfect.\n";
			&maildocument(substr($r, 0, 10));
			exit 1;

#			print ' unknown(' . ($cnt % 2 ? 'y' : 'x') . ':' . $r. ')';
#			last;
		}

		if ($cnt % 2 == 0) {
			$v = $v * $xscale + 5;
			$curx += ($v - 5);
		} else {
			$v = $v * $yscale + 5;
			$cury += ($v - 5);
			&updatebbox($curx, $cury);
		}
		print " $v";
		$r = substr($r, length($m));
		$cnt++;
		$npoints--;
		if ($cnt == 28) {
			print "\n";
			$cnt = 0;
		}
	}
	if ($outputstyle eq 'ps' || $outputstyle eq 'eps') {
		print " ] ST"
	}
	print "\n";
	print "% boundingbox: $boundingx0 $boundingy0 "
		. "$boundingx1 $boundingy1\n" if ($debug);
	print "\n";
}

sub updatebbox {
	local($nx, $ny) = @_;
	$boundingx0 = $nx if ($nx < $boundingx0);
	$boundingy0 = $ny if ($ny < $boundingy0);
	$boundingx1 = $nx if ($nx > $boundingx1);
	$boundingy1 = $ny if ($ny > $boundingy1);
}

sub usage {
	print STDERR <<EOF;
usage: $progname [-abDir] [-s#] [-e#] [-p#] [-w#] filename
EOF
}

sub failure {
	local($msg, $mailit) = @_;
	print STDERR "FATAL: $msg\n";
	if ($mailit) {
		if ($batch) {
			print "contact $reportto for help improving "
				. "the program.\n";
		} else {
			&maildocument;
		}
	}
	exit 1;
}

sub maildocument {
	local($unknownhuff) = @_;
	print STDERR <<EOF
NOTE: automatic emailing function is not implemented yet...
transmit $filename to $reportto, uuencoded.
EOF
}

#
# output style def for each of the mode...
#
sub preamble0 {
	local($func);
	$func = $outputstyle . 'preamble0';
	eval "&$func;";
}

sub postamble0 {
	local($func);
	$func = $outputstyle . 'postamble0';
	eval "&$func;";
}

sub preamble {
	local($func);
	$func = $outputstyle . 'preamble';
	eval "&$func;";

	($curx, $cury) = (0, 0);
}

sub postamble {
	local($func);
	$func = $outputstyle . 'postamble';
	eval "&$func;";
}

sub preprocess {
	local($argv) = @_;
	local($func);
	$func = $outputstyle . 'preprocess';
	eval "&$func;";
}

sub postprocess {
	local($argv) = @_;
	local($func);
	$func = $outputstyle . 'postprocess';
	eval "&$func(\$argv);";
}

sub joinpages {
	local($func);
	$func = $outputstyle . 'joinpages';
	eval "&$func;";
}

sub emitprogram {
	local($l);

	if ($pslinewidth) {
		$l = "$pslinewidth setlinewidth stroke";
	} else {
		$l = 'stroke';
	}
	print <<EOF;
 /y false def
 /NP {newpath moveto} def
 /ST {{  y
         {x exch 5 sub rlineto   /y false def}
         {/x exch 5 sub def     /y true def}
       ifelse}  forall  $l} def
EOF
	if ($outputstyle eq 'ps' && $annotate) {
		print <<EOF;
 /DRAW { newpath 5 3 roll moveto rlineto setlinewidth currentpoint stroke } def
 /pagenumber {
		gsave
		10 20 moveto
		/Courier findfont 10 scalefont setfont show
		grestore
	} def
 /checkbox {
		gsave
		55 20 moveto
		/Courier findfont 10 scalefont setfont show

		3 3 scale 1 setlinecap

		10 5
		0	5.5 0 DRAW
		0	0 5.5 DRAW
		0	-5.5 0 DRAW
		0	0 -5.5 DRAW
		pop pop

		1 0 0 setrgbcolor
		11 10
		1.0	0.5 -1 DRAW
		1.5	0.5 -1 DRAW
		1.8	1 -2 DRAW
		1.8	1 2 DRAW
		1.5	2 3 DRAW
		1.0	1 1 DRAW
		0.5	1 1 DRAW
		pop pop

		0 0 0 setrgbcolor

		grestore
	} def
EOF
	}
}

#
# standalone postscript
#
sub pspreamble0 {
	local($fname) = @_;
	print <<EOF;
%!PS-Adobe-2.0
%%CreationDate: $date
%%Creator: crosspad2ps
%%Orientation: Portrait
%%Pages: (atend)
%%PageOrder: Ascend
%%Title: $filename
%%EndComments
%%BeginFile: $filename

EOF
	&emitprogram;
	print <<EOF;
%%EndFile
%%EndProlog
EOF
}

sub pspreamble {
	local($page) = @_;

	print <<EOF;
%%Page: $page $page
gsave
0 792 translate
0.28346458 -0.28346458 scale
2.0 setlinewidth

EOF
}

sub pspostamble {
	print "grestore\nshowpage\n\n";
}

sub pspostamble0 {
	print <<EOF;
%%Trailer
%%Pages: $pspagenumber
%%EOF
EOF
}

sub pspreprocess {
}

sub pspostprocess {
}

sub psjoinpages {
	local($t, $i, $n);
	local($y0, $y1);
	local(@t);

	die if (! -f "$tmpdir/$startpage");
	die if (! -f "$tmpdir/$startpage.bound");

	select STDOUT;
	print <<EOF;
%!PS-Adobe-2.0
%%CreationDate: $date
%%Creator: crosspad2ps
%%Orientation: Portrait
%%Pages: (atend)
%%DocumentPaperSizes: $papersize
%%PageOrder: Ascend
%%Title: $filename
%%EndComments
%%BeginFile: $filename

EOF
	&emitprogram;
	print <<EOF;
%%EndFile
%%EndProlog
EOF

	opendir(DIR, $tmpdir) || die;
	@t = grep(/^\d+$/, readdir(DIR));
	closedir(DIR);
	$n = 0;
	foreach $i (sort {$a <=> $b} @t) {
		next if ($i < $startpage || $endpage < $i);

		$n++;
		print "%%Page: $n $n\n";
		if ($annotate) {
			print "($n) pagenumber\n";
		}
		if (-f "$tmpdir/$i.bookmark" && $annotate) {
			open(IN, "< $tmpdir/$i.bookmark") ||die;
			$t = <IN>;
			close(IN);
			$t =~ s/\n$//;
			$t =~ s/\s+$//;
			print "($t) checkbox\n";
		}
		print <<EOF;
gsave
0 792 translate
0.28346458 -0.28346458 scale
2.0 setlinewidth
EOF

		open(IN, "< $tmpdir/$i") || die;
		while (<IN>) {
			print;
		}
		close(IN);

		print <<EOF;
grestore
showpage

EOF
	}

	print <<EOF;
%%Trailer
%%Pages: $n
%%MatchingCreationDate: $date
%%EOF
EOF
}

#
# encapsulated postscript
#
sub epspreamble0 {
	local($fname) = @_;
	print <<EOF;
%!
%%CreationDate: $date
%%Creator: crosspad2ps
%%BoundingBox: (atend)

EOF
	&emitprogram;
}

sub epspreamble {
	local($page) = @_;

	print <<EOF;
%%PageBoundingBox: (atend)

1 -1 scale
2.0 setlinewidth

EOF
}

sub epspostamble {
	print "showpage\n\n";
}

sub epspostamble0 {
	print <<EOF;
%%MatchingCreationDate: $date
EOF
}

sub epspreprocess {
	local($t);

	$t = time;
	$tmpfilename = "/tmp/$$.$t";
	open(EPSTMP, "> $tmpfilename") || die;
	select(EPSTMP);
}

sub epspostprocess {
	local($y0, $y1);
	close(EPSTMP);
	open(IN, "< $tmpfilename") || die;
	select STDOUT;

	($y0, $y1) = (-1 * $boundingy0, -1 * $boundingy1);
	while (<IN>) {
		if ($_ =~ /^%%(Page)?BoundingBox: \(atend\)/) {
			print <<EOF;
%%$1BoundingBox: $boundingx0 $y1 $boundingx1 $y0
EOF
		} else {
			print;
		}
	}
	close(IN);
	unlink($tmpfilename) if (!$debug);
}

sub epsjoinpages {
	local($t);
	local($y0, $y1);

	die if ($startpage != $endpage);
	die if (! -f "$tmpdir/$startpage");
	die if (! -f "$tmpdir/$startpage.bound");

	open(IN, "< $tmpdir/$startpage.bound") || die;
	$t = <IN>;
	close(IN);
	($boundingx0, $boundingy0, $boundingx1, $boundingy1) = split(/\s+/, $t);
	($y0, $y1) = (-1 * $boundingy0, -1 * $boundingy1);

	select STDOUT;
	print <<EOF;
%!
%%CreationDate: $date
%%Creator: crosspad2ps
%%BoundingBox: $boundingx0 $y1 $boundingx1 $y0

EOF

	&emitprogram;
	print <<EOF;

%%PageBoundingBox: $boundingx0 $y1 $boundingx1 $y0

1 -1 scale
2.0 setlinewidth

EOF
	open(IN, "< $tmpdir/$startpage") || die;
	while (<IN>) {
		print;
	}
	close(IN);

	print <<EOF;
showpage

%%MatchingCreationDate: $date
EOF
}

#
# tgif "obj" file
#

sub tgifjoinpages {
	local(@t);
	local($separate) = (192);	# must be multiple of 8
	local($width);

	die if ($startpage != $endpage);
	die if (! -f "$tmpdir/$startpage");
	die if (! -f "$tmpdir/$startpage.bound");

	$width = int($pslinewidth ? $pslinewidth : 1);

	open(IN, "< $tmpdir/$startpage.bound") || die;
	$t = <IN>;
	close(IN);
	($boundingx0, $boundingy0, $boundingx1, $boundingy1) = split(/\s+/, $t);

	select STDOUT;

	print <<EOF;
state(0,33,100,0,0,1,16,1,9,1,1,0,0,1,0,1,0,'Courier',0,17,0,0,1,5,0,0,1,1,0,16,1,0,1,1,1,0,1056,1497,0,0,2880).
unit("1 pixel/pixel").
page(1,"",1).
EOF

	$nobj = 1;
	open(IN, "< $tmpdir/$startpage") || die;
	while (<IN>) {
		s/\n$//;
		s/\%.*//;
		s/\s+$//;
		s/^\s+//;
		next if ($_ eq '');

		if (/^START (\d+) (\d+)/) {
			$curx = $1 - $boundingx0;
			$cury = $2 - $boundingy0;
			$curx *= $tgifscale;
			$cury *= $tgifscale;
			@xseq = ($curx);
			@yseq = ($cury);
			$cnt = 1;
		} elsif (/^NPOINTS (\d+)/) {
			$npoints = $1;
		} else {
			@t = split(/\s+/, $_);
			while (scalar(@t)) {
				$xoff = (shift @t) - 5;
				$yoff = (shift @t) - 5;
				$curx += ($xoff * $tgifscale);
				$cury += ($yoff * $tgifscale);
				push(@xseq, $curx);
				push(@yseq, $cury);
				$cnt++;
			}
			if ($cnt == $npoints) {
				$base = 0;
				while ($npoints) {
					$max = ($npoints < $separate) ? $npoints : $separate;
					print "poly('#000000',$max,[";
					$needcomma = 0;
					for ($i = 0; $i < $max; $i++) {
						if ($i % 8 == 0) {
							print "," if ($needcomma);
							print "\n\t";
							$needcomma = 0;
						}
						print "," if ($needcomma);
						print $xseq[$base + $i] . ",";
						print $yseq[$base + $i];
						$needcomma = 1;
					}
					$hinge = '0' x (($max + 3) / 4);
					print <<EOF;
],0,$width,1,$nobj,0,0,0,0,0,3,0,0,0,'$width','8','3',
    "$hinge",[
]).
EOF
					$base += $max;
					$npoints -= $max;
				}
				@xseq = @yseq = ();
				$nobj++;
			}
		}
	}
	close(IN);
}

#
# idraw
#

sub idrawjoinpages {
	local(@t);

	die if ($startpage != $endpage);
	die if (! -f "$tmpdir/$startpage");
	die if (! -f "$tmpdir/$startpage.bound");

	open(IN, "< $tmpdir/$startpage.bound") || die;
	$t = <IN>;
	close(IN);
	($boundingx0, $boundingy0, $boundingx1, $boundingy1) = split(/\s+/, $t);
	($x0, $y1, $x1, $y0) = ($boundingx0, $boundingy0, $boundingx1, $boundingy1);
	$y0 *= -1;
	$y1 *= -1;
	$x1 -= $x0;
	$y1 -= $y0;
	$x1 *= $idrawscale;
	$y1 *= $idrawscale;
	($x0, $y0) = (0, 0);

	select STDOUT;

	print <<EOF;
%!PS-Adobe-2.0 EPSF-1.2
%%Creator: idraw
%%DocumentFonts:
%%Pages: 1
%%BoundingBox: $x0 $y0 $x1 $y1
%%EndComments

%%BeginProcSet: cmpfont.ps 1 2
% composite fonts for ASCII-EUC mixed string
% Version 1.2 1/31/1990
% Author Ken'ichi HANDA (handa\@etl.go.jp)
% Anyone can freely copy, modify, distribute this program.
/copyfont {	% font-dic extra-entry-count  copyfont  font-dic
	1 index maxlength add dict begin
	{	1 index /FID ne 2 index /UniqueID ne and
		{def} {pop pop} ifelse
	} forall
	currentdict
	end
} bind def

/compositefont {	% ASCIIFontName EUCFontName  compositefont  font'
    findfont dup /FontType get 0 eq {
	12 dict begin
	    1 copyfont dup begin
		/Encoding Encoding
		FMapType dup 2 eq {
		    pop 128 128
		}{
		    5 eq {
			256 256
		    }{
			/compositefont errordict /invalidfont get exec
		    } ifelse
		} ifelse
		getinterval def
	    end
	    /eucfont exch definefont
	    exch
	    findfont 1 copyfont dup begin
		/FontMatrix FontMatrix [1 0 0 1 0 0.05] matrix concatmatrix def
	    end
	    /asciifont exch definefont
	    exch
	    /FDepVector [ 4 2 roll ] def
	    /FontType 0 def
	    /WMode 0 def
	    /FMapType 4 def
	    /FontMatrix matrix def
	    /Encoding [0 1] def
	    /FontBBox {0 0 0 0} def
	    currentdict
	end
    }{
	pop findfont 0 copyfont
    } ifelse
} def	

/slantfont {	% FontName slant-degree  slantfont  font'
    exch findfont 1 copyfont begin
    [ 1 0 4 -1 roll 1 0 0 ] FontMatrix exch matrix concatmatrix
    /FontMatrix exch def
    currentdict
    end
} def
%%EndProcSet

%%BeginProcSet: idraw 3 0
/IdrawDict 50 dict def
IdrawDict begin

/arrowHeight 8 def
/arrowWidth 4 def

/none null def
/numGraphicParameters 17 def
/stringLimit 65535 def

/Begin {
save
numGraphicParameters dict begin
} def

/End {
end
restore
} def

/SetB {
dup type /nulltype eq {
pop
false /brushRightArrow idef
false /brushLeftArrow idef
true /brushNone idef
} {
/brushDashOffset idef
/brushDashArray idef
0 ne /brushRightArrow idef
0 ne /brushLeftArrow idef
/brushWidth idef
false /brushNone idef
} ifelse
} def

/SetCFg {
/fgblue idef
/fggreen idef
/fgred idef
} def

/SetCBg {
/bgblue idef
/bggreen idef
/bgred idef
} def

/SetF {
/printSize idef
/printFont idef
} def

/SetP {
dup type /nulltype eq {
pop true /patternNone idef
} {
dup -1 eq {
/patternGrayLevel idef
/patternString idef
} {
/patternGrayLevel idef
} ifelse
false /patternNone idef
} ifelse
} def

/BSpl {
0 begin
storexyn
newpath
n 1 gt {
0 0 0 0 0 0 1 1 true subspline
n 2 gt {
0 0 0 0 1 1 2 2 false subspline
1 1 n 3 sub {
/i exch def
i 1 sub dup i dup i 1 add dup i 2 add dup false subspline
} for
n 3 sub dup n 2 sub dup n 1 sub dup 2 copy false subspline
} if
n 2 sub dup n 1 sub dup 2 copy 2 copy false subspline
patternNone not brushLeftArrow not brushRightArrow not and and { ifill } if
brushNone not { istroke } if
0 0 1 1 leftarrow
n 2 sub dup n 1 sub dup rightarrow
} if
end
} dup 0 4 dict put def

/Circ {
newpath
0 360 arc
patternNone not { ifill } if
brushNone not { istroke } if
} def

/CBSpl {
0 begin
dup 2 gt {
storexyn
newpath
n 1 sub dup 0 0 1 1 2 2 true subspline
1 1 n 3 sub {
/i exch def
i 1 sub dup i dup i 1 add dup i 2 add dup false subspline
} for
n 3 sub dup n 2 sub dup n 1 sub dup 0 0 false subspline
n 2 sub dup n 1 sub dup 0 0 1 1 false subspline
patternNone not { ifill } if
brushNone not { istroke } if
} {
Poly
} ifelse
end
} dup 0 4 dict put def

/Elli {
0 begin
newpath
4 2 roll
translate
scale
0 0 1 0 360 arc
patternNone not { ifill } if
brushNone not { istroke } if
end
} dup 0 1 dict put def

/Line {
0 begin
2 storexyn
newpath
x 0 get y 0 get moveto
x 1 get y 1 get lineto
brushNone not { istroke } if
0 0 1 1 leftarrow
0 0 1 1 rightarrow
end
} dup 0 4 dict put def

/MLine {
0 begin
storexyn
newpath
n 1 gt {
x 0 get y 0 get moveto
1 1 n 1 sub {
/i exch def
x i get y i get lineto
} for
patternNone not brushLeftArrow not brushRightArrow not and and { ifill } if
brushNone not { istroke } if
0 0 1 1 leftarrow
n 2 sub dup n 1 sub dup rightarrow
} if
end
} dup 0 4 dict put def

/Poly {
3 1 roll
newpath
moveto
-1 add
{ lineto } repeat
closepath
patternNone not { ifill } if
brushNone not { istroke } if
} def

/Rect {
0 begin
/t exch def
/r exch def
/b exch def
/l exch def
newpath
l b moveto
l t lineto
r t lineto
r b lineto
closepath
patternNone not { ifill } if
brushNone not { istroke } if
end
} dup 0 4 dict put def

/Text {
ishow
} def

/idef {
dup where { pop pop pop } { exch def } ifelse
} def

/ifill {
0 begin
gsave
patternGrayLevel -1 ne {
fgred bgred fgred sub patternGrayLevel mul add
fggreen bggreen fggreen sub patternGrayLevel mul add
fgblue bgblue fgblue sub patternGrayLevel mul add setrgbcolor
eofill
} {
eoclip
originalCTM setmatrix
pathbbox /t exch def /r exch def /b exch def /l exch def
/w r l sub ceiling cvi def
/h t b sub ceiling cvi def
/imageByteWidth w 8 div ceiling cvi def
/imageHeight h def
bgred bggreen bgblue setrgbcolor
eofill
fgred fggreen fgblue setrgbcolor
w 0 gt h 0 gt and {
l w add b translate w neg h scale
w h true [w 0 0 h neg 0 h] { patternproc } imagemask
} if
} ifelse
grestore
end
} dup 0 8 dict put def

/istroke {
gsave
brushDashOffset -1 eq {
[] 0 setdash
1 setgray
} {
brushDashArray brushDashOffset setdash
fgred fggreen fgblue setrgbcolor
} ifelse
brushWidth setlinewidth
originalCTM setmatrix
stroke
grestore
} def

/ishow {
0 begin
gsave
fgred fggreen fgblue setrgbcolor
/fontDict printFont printSize scalefont dup setfont def
/descender fontDict begin 0 [FontBBox] 1 get FontMatrix end
transform exch pop def
/vertoffset 1 printSize sub descender sub def {
0 vertoffset moveto show
/vertoffset vertoffset printSize sub def
} forall
grestore
end
} dup 0 3 dict put def
/patternproc {
0 begin
/patternByteLength patternString length def
/patternHeight patternByteLength 8 mul sqrt cvi def
/patternWidth patternHeight def
/patternByteWidth patternWidth 8 idiv def
/imageByteMaxLength imageByteWidth imageHeight mul
stringLimit patternByteWidth sub min def
/imageMaxHeight imageByteMaxLength imageByteWidth idiv patternHeight idiv
patternHeight mul patternHeight max def
/imageHeight imageHeight imageMaxHeight sub store
/imageString imageByteWidth imageMaxHeight mul patternByteWidth add string def
0 1 imageMaxHeight 1 sub {
/y exch def
/patternRow y patternByteWidth mul patternByteLength mod def
/patternRowString patternString patternRow patternByteWidth getinterval def
/imageRow y imageByteWidth mul def
0 patternByteWidth imageByteWidth 1 sub {
/x exch def
imageString imageRow x add patternRowString putinterval
} for
} for
imageString
end
} dup 0 12 dict put def

/min {
dup 3 2 roll dup 4 3 roll lt { exch } if pop
} def

/max {
dup 3 2 roll dup 4 3 roll gt { exch } if pop
} def

/midpoint {
0 begin
/y1 exch def
/x1 exch def
/y0 exch def
/x0 exch def
x0 x1 add 2 div
y0 y1 add 2 div
end
} dup 0 4 dict put def

/thirdpoint {
0 begin
/y1 exch def
/x1 exch def
/y0 exch def
/x0 exch def
x0 2 mul x1 add 3 div
y0 2 mul y1 add 3 div
end
} dup 0 4 dict put def

/subspline {
0 begin
/movetoNeeded exch def
y exch get /y3 exch def
x exch get /x3 exch def
y exch get /y2 exch def
x exch get /x2 exch def
y exch get /y1 exch def
x exch get /x1 exch def
y exch get /y0 exch def
x exch get /x0 exch def
x1 y1 x2 y2 thirdpoint
/p1y exch def
/p1x exch def
x2 y2 x1 y1 thirdpoint
/p2y exch def
/p2x exch def
x1 y1 x0 y0 thirdpoint
p1x p1y midpoint
/p0y exch def
/p0x exch def
x2 y2 x3 y3 thirdpoint
p2x p2y midpoint
/p3y exch def
/p3x exch def
movetoNeeded { p0x p0y moveto } if
p1x p1y p2x p2y p3x p3y curveto
end
} dup 0 17 dict put def

/storexyn {
/n exch def
/y n array def
/x n array def
n 1 sub -1 0 {
/i exch def
y i 3 2 roll put
x i 3 2 roll put
} for
} def

/SSten {
fgred fggreen fgblue setrgbcolor
dup true exch 1 0 0 -1 0 6 -1 roll matrix astore
} def

/FSten {
dup 3 -1 roll dup 4 1 roll exch
newpath
0 0 moveto
dup 0 exch lineto
exch dup 3 1 roll exch lineto
0 lineto
closepath
bgred bggreen bgblue setrgbcolor
eofill
SSten
} def

/Rast {
exch dup 3 1 roll 1 0 0 -1 0 6 -1 roll matrix astore
} def

/arrowhead {
0 begin
transform originalCTM itransform
/taily exch def
/tailx exch def
transform originalCTM itransform
/tipy exch def
/tipx exch def
/dy tipy taily sub def
/dx tipx tailx sub def
/angle dx 0 ne dy 0 ne or { dy dx atan } { 90 } ifelse def
gsave
originalCTM setmatrix
tipx tipy translate
angle rotate
newpath
arrowHeight neg arrowWidth 2 div moveto
0 0 lineto
arrowHeight neg arrowWidth 2 div neg lineto
patternNone not {
originalCTM setmatrix
/padtip arrowHeight 2 exp 0.25 arrowWidth 2 exp mul add sqrt brushWidth mul
arrowWidth div def
/padtail brushWidth 2 div def
tipx tipy translate
angle rotate
padtip 0 translate
arrowHeight padtip add padtail add arrowHeight div dup scale
arrowheadpath
ifill
} if
brushNone not {
originalCTM setmatrix
tipx tipy translate
angle rotate
arrowheadpath
istroke
} if
grestore
end
} dup 0 9 dict put def

/arrowheadpath {
newpath
arrowHeight neg arrowWidth 2 div moveto
0 0 lineto
arrowHeight neg arrowWidth 2 div neg lineto
} def

/leftarrow {
0 begin
y exch get /taily exch def
x exch get /tailx exch def
y exch get /tipy exch def
x exch get /tipx exch def
brushLeftArrow { tipx tipy tailx taily arrowhead } if
end
} dup 0 4 dict put def

/rightarrow {
0 begin
y exch get /tipy exch def
x exch get /tipx exch def
y exch get /taily exch def
x exch get /tailx exch def
brushRightArrow { tipx tipy tailx taily arrowhead } if
end
} dup 0 4 dict put def

end
%%EndProcSet
%%EndProlog

%%BeginSetup
IdrawDict begin

%%EndSetup

%I Idraw 10 Grid 8 8 

%%Page: 1 1

Begin
%I b u
%I cfg u
%I cbg u
%I f u
%I p u
%I t
[ 0.956649 0 0 0.956649 0 0 ] concat
/originalCTM matrix currentmatrix def

EOF

	$nobj = 1;
	open(IN, "< $tmpdir/$startpage") || die;
	while (<IN>) {
		s/\n$//;
		s/\%.*//;
		s/\s+$//;
		s/^\s+//;
		next if ($_ eq '');

		if (/^START (\d+) (\d+)/) {
			$curx = $1 - $boundingx0;
			$cury = $boundingy1 - $2;	# flip
			$curx *= $idrawscale;
			$cury *= $idrawscale;
		} elsif (/^NPOINTS (\d+)/) {
			$npoints = $1;
			print <<EOF;
Begin %I MLine
%I b 65535
0 0 0 [] 0 SetB
%I cfg Black
0 0 0 SetCFg
%I cbg White
1 1 1 SetCBg
none SetP %I p n
%I t
[ 1 -0 -0 1 27 174 ] concat
%I $npoints
EOF
			printf("%d %d\n", int($curx), int($cury));
			$cnt = 0;
		} else {
			@t = split(/\s+/, $_);
			while (scalar(@t)) {
				$xoff = (shift @t) - 5;
				$yoff = (shift @t) - 5;
				$curx += ($xoff * $idrawscale);
				$cury -= ($yoff * $idrawscale);	# flip
				printf("%d %d\n", int($curx), int($cury));
				$cnt++;
			}
			$hinge = '0' x (($npoints + 3) / 4);
			if ($cnt == $npoints - 1) {
				print <<EOF;
$npoints MLine
%I 1
End

EOF
				$nobj++;
			}
		}
	}
	print <<EOF;
End %I eop

showpage

%%Trailer

end
EOF
	close(IN);
}
