📜 ⬆️ ⬇️

Your Web-PDF printer in 10 minutes



How to spend quite a bit of time and do something simple and original, striking in its globality - but absolutely useless? Very simple. Let's make your printer.

We will 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? Go!
')

Create a printer script in the cgi-bin daddy. The content of the script is very simple:

<br> #! /usr/bin/perl <br><br> use strict ; <br><br> if (! defined ( $ENV { 'CONTENT_TYPE' } ) || $ENV { 'CONTENT_TYPE' } ne "application/ipp" ) { <br> print "Content-Type: text/html\n\n" ; <br> print ":-)" ; <br> exit ; <br> } <br><br> # $d - <br> # $l - <br> # $i - <br> # %a - <br><br> my ( $d , $l , $i , %a ) = ( "" , 0 , 0 ); <br> binmode STDIN ; <br> $l += read ( STDIN , $d , 4096 , $l ) while (! eof ( STDIN )); <br><br> parseRequest (\ $d , \ $l , \ $i , \ %a ); <br><br> my $o = "" <br> . substr ( $d , 0 , 2 ) # version <br> . chr ( 0x00 ) . chr ( 0x00 ) # status <br> . substr ( $d , 4 , 4 ) # request <br> . chr ( 0x01 ) # attributes <br> . stringAttribute ( 0x47 , "attributes-charset" , "utf-8" ) <br> . stringAttribute ( 0x48 , "attributes-natural-language" , "en-us" ) <br> . chr ( 0x04 ) # attributes <br> . stringAttribute ( 0x42 , "printer-name" , "PDF" ) <br> . chr ( 0x03 ) # end <br> . chr ( 0x0a ) <br> ; <br><br> print "Content-Type: text/html\n" ; <br> print "Content-Length: " . length ( $o ) . "\n" ; <br> print "\n" ; <br> print $o ; <br><br> if ( defined ( $a { '-status' } ) && $a { '-status' } == 0x02 && $i < $l ) { <br> my @t = localtime ; <br> my $output = sprintf ( "../pdf/%04d%02d%02d-%02d%02d%02d.pdf" , $t [ 5 ] + 1900 , $t [ 4 ], $t [ 3 ], $t [ 2 ], $t [ 1 ], $t [ 0 ]); <br> if ( open ( P , "|-" , "gs" , "-q" , "-dBATCH" , "-dNOPAUSE" , "-dSAFER" , "-sDEVICE=pdfwrite" , "-sOutputFile=$output" , "-" )) { <br> binmode P ; <br> print P substr ( $d , $i ); <br> close P ; <br> } <br> } <br><br> sub parseRequest { <br> my ( $d , $l , $i , $a ) = @_ ; <br> return if $ $i >= $ $l - 2 ; <br> $ $a { '-version' } = ( ord ( substr ( $ $d , $ $i , 1 )) << 8 ) + ord ( substr ( $ $d , $ $i + 1 , 1 )); $ $i += 2 ; <br> return if $ $i >= $ $l - 2 ; <br> $ $a { '-status' } = ( ord ( substr ( $ $d , $ $i , 1 )) << 8 ) + ord ( substr ( $ $d , $ $i + 1 , 1 )); $ $i += 2 ; <br> return if $ $i >= $ $l - 4 ; <br> $ $a { '-request' } = parseInt ( substr ( $ $d , $ $i , 4 )); $ $i += 4 ; <br> return if $ $i >= $ $l - 1 ; <br> my $what = ord ( substr ( $ $d , $ $i , 1 )); $ $i ++; <br> return parseAttributes ( $d , $l , $i , $a ) if ( $what == 0x01 ); <br> } <br><br> sub parseAttributes { <br> my ( $d , $l , $i , $a ) = @_ ; <br> while ( $ $i < $ $l ) { <br> my $what = ord ( substr ( $ $d , $ $i , 1 )); $ $i ++; <br> return if ( $what == 0x03 ); <br> return parseAttributes ( $d , $l , $i , $a ) if ( $what == 0x02 ); <br> return parseAttributes ( $d , $l , $i , $a ) if ( $what == 0x04 ); <br> return if $ $i >= $ $l - 2 ; <br> my $key_len = ( ord ( substr ( $ $d , $ $i , 1 )) << 8 ) + ord ( substr ( $ $d , $ $i + 1 , 1 )); $ $i += 2 ; <br> return if $ $i >= $ $l - $key_len ; <br> my $key = substr ( $ $d , $ $i , $key_len ); $ $i += $key_len ; <br> return if $ $i >= $ $l - 2 ; <br> my $val_len = ( ord ( substr ( $ $d , $ $i , 1 )) << 8 ) + ord ( substr ( $ $d , $ $i + 1 , 1 )); $ $i += 2 ; <br> return if $ $i >= $ $l - $val_len ; <br> my $val = substr ( $ $d , $ $i , $val_len ); $ $i += $val_len ; <br> $ $a { $key } = $val ; <br> } <br> } <br><br> sub parseInt { <br> my $v = shift ; <br> my $l = length ( $v ); <br> my $r = 0 ; <br> for ( my $i = $l ; $i > 0 ; $i --) { <br> $r += ( ( 1 << (( $i - 1 ) * 8 )) * ord ( substr ( $v , $l - $i , 1 )) ); <br> } <br> $r -= 4294967296 if ( $r >= 2147483648 ); <br> return $r ; <br> } <br><br> sub stringLength { <br> my $s = shift ; <br> my $l = length ( $s ); <br> my $i1 = $l & 0xFF ; <br> $l = ( $l - $i1 ) >> 8 ; <br> my $i2 = $l & 0xFF ; <br> return chr ( $i2 ) . chr ( $i1 ); <br> } <br><br> sub stringAttribute { <br> my ( $type , $key , $val ) = @_ ; <br> return chr ( $type ) . stringLength ( $key ) . $key . stringLength ( $val ) . $val ; <br> } <br>

As you can see, there are no external dependencies on Perl modules, and nothing extra. In fact, only Perl and the gs program are needed, which are practically everywhere.

The script was once based on PHP :: Print :: IPP . But, since on most servers it is forbidden to execute external programs from PHP scripts, I had to rewrite to Perl. The script implements the most basic functionality of the IPP server.

Next, we give the rights to execute the script ( 755 , or rwxr-xr-x ). We look in the browser: http://www.site.ru/cgi-bin/printer . Works? Good.

We will 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:
And print a test page. In the pdf daddy on the server appears our test page.

Similarly, many things can be printed. Documents, pictures ... Anything.

It remains to make three comments.

1. Theoretically, the script may not work, for various reasons. The basic principles of debugging Perl scripts leave for self-study.
2. It is advisable to rename the printer script to something more tricky so that everyone does not print on your printer. Using .htaccess it is rather difficult to close access to the CGI script, to make authorization inside the Perl script too. And that could be ...
3. The printer works fine under Windows Vista and Linux. Installation, in general, is not so difficult. But is it necessary? ..

UPD: By popular demand, I post a version for PHP.
You must enable php.ini always_populate_raw_post_data = On and not disable the ability to perform popen .

<? <br><br> if (! isset ( $_SERVER [ 'CONTENT_TYPE' ]) || $_SERVER [ 'CONTENT_TYPE' ] != "application/ipp" ) { <br> header ( "Content-Type: text/html" ); <br> print ":-)" ; <br> exit ; <br> } <br><br> # $d - <br> # $l - <br> # $i - <br> # %a - <br><br> $d = & $HTTP_RAW_POST_DATA ; <br> $l = strlen ( $d ); <br> $i = 0 ; <br> $a = array (); <br><br> parseRequest ( $d , $l , $i , $a ); <br><br> $o = "" <br> . substr ( $d , 0 , 2 ) # version <br> . chr ( 0x00 ) . chr ( 0x00 ) # status <br> . substr ( $d , 4 , 4 ) # request <br> . chr ( 0x01 ) # attributes <br> . stringAttribute ( 0x47 , "attributes-charset" , "utf-8" ) <br> . stringAttribute ( 0x48 , "attributes-natural-language" , "en-us" ) <br> . chr ( 0x04 ) # attributes <br> . stringAttribute ( 0x42 , "printer-name" , "PDF" ) <br> . chr ( 0x03 ) # end <br> . chr ( 0x0a ) <br> ; <br><br> header ( "Content-Type: text/html" ); <br> header ( "Content-Length: " . strlen ( $o )); <br> print $o ; <br><br> if ( isset ( $a [ '-status' ]) && $a [ '-status' ] == 0x02 && $i < $l ) { <br> $output = sprintf ( "pdf/%s.pdf" , date ( "Ymd-His" )); <br> if ( $P = popen ( "gs -q -dBATCH -dNOPAUSE -dSAFER -sDEVICE=pdfwrite -sOutputFile=$output -" , "w" )) { <br> fwrite ( $P , substr ( $d , $i )); <br> fclose ( $P ); <br> } <br> } <br><br> function parseRequest (& $d , & $l , & $i , & $a ) { <br> if ( $i >= $l - 2 ) return ; <br> $a [ '-version' ] = ( ord ( $d [ $i ]) << 8 ) + ord ( $d [ $i + 1 ]); $i += 2 ; <br> if ( $i >= $l - 2 ) return ; <br> $a [ '-status' ] = ( ord ( $d [ $i ]) << 8 ) + ord ( $d [ $i + 1 ]); $i += 2 ; <br> if ( $i >= $l - 4 ) return ; <br> $a [ '-request' ] = parseInt ( substr ( $d , $i , 4 )); $i += 4 ; <br> if ( $i >= $l - 1 ) return ; <br> $what = ord ( $d [ $i ]); $i ++; <br> if ( $what == 0x01 ) return parseAttributes ( $d , $l , $i , $a ); <br> } <br><br> function parseAttributes (& $d , & $l , & $i , & $a ) { <br> while ( $i < $l ) { <br> $what = ord ( $d [ $i ]); $i ++; <br> if ( $what == 0x03 ) return ; <br> if ( $what == 0x02 ) return parseAttributes ( $d , $l , $i , $a ); <br> if ( $what == 0x04 ) return parseAttributes ( $d , $l , $i , $a ); <br> if ( $i >= $l - 2 ) return ; <br> $key_len = ( ord ( $d [ $i ]) << 8 ) + ord ( $d [ $i + 1 ]); $i += 2 ; <br> if ( $i >= $l - $key_len ) return ; <br> $key = substr ( $d , $i , $key_len ); $i += $key_len ; <br> if ( $i >= $l - 2 ) return ; <br> $val_len = ( ord ( $d [ $i ]) << 8 ) + ord ( $d [ $i + 1 ]); $i += 2 ; <br> if ( $i >= $l - $val_len ) return ; <br> $val = substr ( $d , $i , $val_len ); $i += $val_len ; <br> $a [ $key ] = $val ; <br> } <br> } <br><br> function parseInt ( $v ) { <br> $r = 0 ; <br> $l = strlen ( $v ); <br> for ( $i = $l ; $i > 0 ; $i --) { <br> $r += ( ( 1 << (( $i - 1 ) * 8 )) * ord ( $v [ $l - $i ]) ); <br> } <br> if ( $r >= 2147483648 ) $r -= 4294967296 ; <br> return $r ; <br> } <br><br> function stringLength ( $s ) { <br> $l = strlen ( $s ); <br> $i1 = $l & 0xFF ; <br> $l = ( $l - $i1 ) >> 8 ; <br> $i2 = $l & 0xFF ; <br> return chr ( $i2 ) . chr ( $i1 ); <br> } <br><br> function stringAttribute ( $type , $key , $val ) { <br> return chr ( $type ) . stringLength ( $key ) . $key . stringLength ( $val ) . $val ; <br> } <br><br> ?> <br>
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. In this case, slashes in the path should be reversed: "C: \\ Program files \\\ gs \\ gs8.71 \\ bin \\ gs.exe". The same goes for the PDF file. Because, while PHP itself understands files with direct slashes, this cannot be said about the Windows command line interpreter.

Source: https://habr.com/ru/post/102124/


All Articles