
Your Web-PDF printer in 10 minutes

How to spend quite a bit of time and do something simple and original, striking in its global nature - but absolutely useless? Very simple. Let's make our printer.
We need (except for the head and hands) only a working web server with cgi-bin support , to which we have access via FTP . Is there such a thing? Go!
Create a printer script in the cgi-bin folder . The content of the script is very simple: As you can see, there are no external dependencies on Perl modules, and nothing more. In fact, you only need Perl and the gs program , which are almost everywhere. The script was once based on PHP :: Print :: IPP
#! /usr/bin/perl
use strict;
if (!defined($ENV{'CONTENT_TYPE'}) || $ENV{'CONTENT_TYPE'} ne "application/ipp") {
print "Content-Type: text/html\n\n";
print ":-)";
exit;
}
# $d - входные данные в виде строки
# $l - длина строки входных данных
# $i - текущая позиция разбора данных
# %a - разобранные атрибуты
my ($d, $l, $i, %a) = ("", 0, 0);
binmode STDIN;
$l += read(STDIN, $d, 4096, $l) while (!eof(STDIN));
parseRequest(\$d, \$l, \$i, \%a);
my $o = ""
. substr($d, 0, 2) # version
. chr(0x00) . chr(0x00) # status
. substr($d, 4, 4) # request
. chr(0x01) # attributes
. stringAttribute(0x47, "attributes-charset", "utf-8")
. stringAttribute(0x48, "attributes-natural-language", "en-us")
. chr(0x04) # attributes
. stringAttribute(0x42, "printer-name", "PDF")
. chr(0x03) # end
. chr(0x0a)
;
print "Content-Type: text/html\n";
print "Content-Length: " . length($o) . "\n";
print "\n";
print $o;
if (defined($a{'-status'}) && $a{'-status'} == 0x02 && $i < $l) {
my @t = localtime;
my $output = sprintf("../pdf/%04d%02d%02d-%02d%02d%02d.pdf", $t[5] + 1900, $t[4], $t[3], $t[2], $t[1], $t[0]);
if (open(P, "|-", "gs", "-q", "-dBATCH", "-dNOPAUSE", "-dSAFER", "-sDEVICE=pdfwrite", "-sOutputFile=$output", "-")) {
binmode P;
print P substr($d, $i);
close P;
}
}
sub parseRequest {
my ($d, $l, $i, $a) = @_;
returnif $$i >= $$l - 2;
$$a{'-version'} = (ord(substr($$d, $$i, 1)) << 8) + ord(substr($$d, $$i + 1, 1)); $$i += 2;
returnif $$i >= $$l - 2;
$$a{'-status'} = (ord(substr($$d, $$i, 1)) << 8) + ord(substr($$d, $$i + 1, 1)); $$i += 2;
returnif $$i >= $$l - 4;
$$a{'-request'} = parseInt(substr($$d, $$i, 4)); $$i += 4;
returnif $$i >= $$l - 1;
my $what = ord(substr($$d, $$i, 1)); $$i ++;
returnparseAttributes($d, $l, $i, $a) if ($what == 0x01);
}
sub parseAttributes {
my ($d, $l, $i, $a) = @_;
while ($$i < $$l) {
my $what = ord(substr($$d, $$i, 1)); $$i ++;
returnif ($what == 0x03);
returnparseAttributes($d, $l, $i, $a) if ($what == 0x02);
returnparseAttributes($d, $l, $i, $a) if ($what == 0x04);
returnif $$i >= $$l - 2;
my $key_len = (ord(substr($$d, $$i, 1)) << 8) + ord(substr($$d, $$i + 1, 1)); $$i += 2;
returnif $$i >= $$l - $key_len;
my $key = substr($$d, $$i, $key_len); $$i += $key_len;
returnif $$i >= $$l - 2;
my $val_len = (ord(substr($$d, $$i, 1)) << 8) + ord(substr($$d, $$i + 1, 1)); $$i += 2;
returnif $$i >= $$l - $val_len;
my $val = substr($$d, $$i, $val_len); $$i += $val_len;
$$a{$key} = $val;
}
}
sub parseInt {
my $v = shift;
my $l = length($v);
my $r = 0;
for (my $i = $l; $i > 0; $i --) {
$r += ( (1 << (($i - 1) * 8)) * ord(substr($v, $l - $i, 1)) );
}
$r -= 4294967296 if ($r >= 2147483648);
return $r;
}
sub stringLength {
my $s = shift;
my $l = length($s);
my $i1 = $l & 0xFF;
$l = ($l - $i1) >> 8;
my $i2 = $l & 0xFF;
returnchr($i2) . chr($i1);
}
sub stringAttribute {
my ($type, $key, $val) = @_;
returnchr($type) . stringLength($key) . $key . stringLength($val) . $val;
}
. But, since it is forbidden to execute external programs from PHP scripts on most servers, I had to rewrite it to Perl. The script implements the very, most basic functionality of an IPP server.
Next, we give the right to execute the script ( 755 , or rwxr-xr-x ). We look in the browser: http://www.site.ru/cgi-bin/printer . Works? Good.
We also create a pdf folder in the root of the site and set write permissions to this folder ( 777 , or rwxrwxrwx ).
Now add the printer to Windows:
- Printer Setup
- A network printer or a printer connected to another computer
- Connect to the printer on the Internet, on your home network, or on the intranet: http://www.site.ru/cgi-bin/printer
- Manufacturer: Generic , Model: MS Publisher Imagesetter
Similarly, you can print a lot of things. Documents, pictures ... Anything.
It remains to make three comments.
1. Theoretically, the script may not work, for a variety of reasons. I will leave the basic principles of debugging Perl scripts for independent study.
2. It is advisable to rename the printer script to some trickier one so that everyone doesn’t print on your printer. It is quite difficult to close access to the CGI script through .htaccess ; authorization inside the Perl script is also difficult. And then it would be possible ...
3.The printer works fine under Windows Vista and Linux. Installation, in general, is not so complicated. But is this necessary? ..
UPD: By popular demand, I am posting a version in PHP.
You must enable always_populate_raw_post_data = On in php.ini and not disable the ability to execute popen . For advanced users. If you try to run the script on a Windows server, you may need to specify the full path to the gs interpreter . At the same time, slashes in the path must be inverse: "C: \\ Program files \\\ gs \\ gs8.71 \\ bin \\ gs.exe". The same goes for the PDF file. Because, even though PHP itself understands files with forward slashes, this cannot be said about the Windows command line interpreter.
if (!isset($_SERVER['CONTENT_TYPE']) || $_SERVER['CONTENT_TYPE'] != "application/ipp") {
header("Content-Type: text/html");
print ":-)";
exit;
}
# $d - входные данные в виде строки
# $l - длина строки входных данных
# $i - текущая позиция разбора данных
# %a - разобранные атрибуты
$d = &$HTTP_RAW_POST_DATA;
$l = strlen($d);
$i = 0;
$a = array();
parseRequest($d, $l, $i, $a);
$o = ""
. substr($d, 0, 2) # version
. chr(0x00) . chr(0x00) # status
. substr($d, 4, 4) # request
. chr(0x01) # attributes
. stringAttribute(0x47, "attributes-charset", "utf-8")
. stringAttribute(0x48, "attributes-natural-language", "en-us")
. chr(0x04) # attributes
. stringAttribute(0x42, "printer-name", "PDF")
. chr(0x03) # end
. chr(0x0a)
;
header("Content-Type: text/html");
header("Content-Length: " . strlen($o));
print $o;
if (isset($a['-status']) && $a['-status'] == 0x02 && $i < $l) {
$output = sprintf("pdf/%s.pdf", date("Ymd-His"));
if ($P = popen("gs -q -dBATCH -dNOPAUSE -dSAFER -sDEVICE=pdfwrite -sOutputFile=$output -", "w")) {
fwrite($P, substr($d, $i));
fclose($P);
}
}
functionparseRequest(&$d, &$l, &$i, &$a) {
if ($i >= $l - 2) return;
$a['-version'] = (ord($d[$i]) << 8) + ord($d[$i + 1]); $i += 2;
if ($i >= $l - 2) return;
$a['-status'] = (ord($d[$i]) << 8) + ord($d[$i + 1]); $i += 2;
if ($i >= $l - 4) return;
$a['-request'] = parseInt(substr($d, $i, 4)); $i += 4;
if ($i >= $l - 1) return;
$what = ord($d[$i]); $i ++;
if ($what == 0x01) returnparseAttributes($d, $l, $i, $a);
}
functionparseAttributes(&$d, &$l, &$i, &$a) {
while ($i < $l) {
$what = ord($d[$i]); $i ++;
if ($what == 0x03) return;
if ($what == 0x02) returnparseAttributes($d, $l, $i, $a);
if ($what == 0x04) returnparseAttributes($d, $l, $i, $a);
if ($i >= $l - 2) return;
$key_len = (ord($d[$i]) << 8) + ord($d[$i + 1]); $i += 2;
if ($i >= $l - $key_len) return;
$key = substr($d, $i, $key_len); $i += $key_len;
if ($i >= $l - 2) return;
$val_len = (ord($d[$i]) << 8) + ord($d[$i + 1]); $i += 2;
if ($i >= $l - $val_len) return;
$val = substr($d, $i, $val_len); $i += $val_len;
$a[$key] = $val;
}
}
functionparseInt($v) {
$r = 0;
$l = strlen($v);
for ($i = $l; $i > 0; $i --) {
$r += ( (1 << (($i - 1) * 8)) * ord($v[$l - $i]) );
}
if ($r >= 2147483648) $r -= 4294967296;
return $r;
}
functionstringLength($s) {
$l = strlen($s);
$i1 = $l & 0xFF;
$l = ($l - $i1) >> 8;
$i2 = $l & 0xFF;
returnchr($i2) . chr($i1);
}
functionstringAttribute($type, $key, $val) {
returnchr($type) . stringLength($key) . $key . stringLength($val) . $val;
}
?>