<?php
/**
* The Paper Tape Project -- Visualisation Web frontend
*
* This PHP setup is capable of serving PNG and SVG files to clients
* by using the cli.c interface to the LOCHSTREIFEN cairo drawing
* routines. See index.html for all possible GET parameters.
*
* Currently this is a german-only subproject.
*
* Copyright (C) 2008 Sven Köppel
*
* This program is free software; you can redistribute
* it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General
* Public License along with this program; if not, see
* <http://www.gnu.org/licenses/>.
*
**/
// Konstanten:
$draw_program = "../visualisator/binary";
// Ausgabeordner (ohne trailing slash)
$output_dir = "temp";
// mit Verbose-Switch wird der Programmaufruf verbose.
$verbose = isset($_GET['verbose']) || isset($_POST['verbose']);
$ajax = isset($_GET['ajax']); // ajax-antwort oder normale Webseite anzeigen?
if(!$ajax)
echo makeHeader('Lochstreifenvisualisierung online');
function makeHeader($title) {
// als Funktion wg. Report-Datei, die später noch generiert wird.
return <<<HERE
<html>
<head>
<title>$title - technikum29 Private Zone</title>
<link rel="stylesheet" href="http://dev.technikum29.de/src/private.design/style.css" type="text/css">
<link rel="stylesheet" href="design/formular.css" type="text/css">
<!--<script type="text/javascript" src="design/formular.js"></script>--><!-- hier nicht mehr -->
<meta name="date.initial" value="02.04.2008">
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
</head>
<body>
<h1><a href="../"><span>technikum29.de interne Projekte:</span></a><strong>Lochstreifenprojekt: Visualisierung</strong></h1>
HERE;
}
/*
* Übersicht über POST-Parameter, die entgegen genommen werden:
*'data-src' = file|input|font
-> file => 'data-file' (dateiupload)
-> form => 'data-form' = hex[0]|oct|bin|dec
'data-input' = auf Länge checken und format
-> font => 'data-text' = auf gute Zeichen checken ([a-zA-Z0-9 ])
*'format' = PNG|SVG
*'dimension-src' = width|height|diameter
-> width => dimension-width
-> height => dimension-height jeweils auf int checken
-> diameter => dimension-diameter
*'alignment' => hor-[rtl|ltr]-[l|r] | ver-[btt|ttb]-[u|o]
komponenten = imagebg|tapebg|punched|notpunched|feedholes
*'show-'+[komponenten] (nur toggle an/aus)
*'color-'+[komponenten] => #RRGGBB
*'lucency-'+[komponenten] => AA
*'empty-start', 'empty-end' => int
*/
if(empty($_POST)) {
// so einfach ist das.
print '<h2>Parameter fehlen</h2>';
exit; // ;)
}
function get($var) {
// kleine Helperfunktion
return isset($_POST[$var]) ? $_POST[$var] : false;
}
function my_ctype_digit($string) {
// meine Implementierung dieser netten Funkion
return preg_match('/^[0-9]+$/', $string);
}
// Sofort tötende Fehler. Anzuwenden bei brutalen Fehlern, die nicht
// auf Fehleingabe im Formular berufen (Angriffsgefahr)
function print_fatal($text) {
print "<h2>Falsche Benutzung!</h2>\n";
print '<p class="big-error">'.$text."</p>\n";
print "<p>Dieser Fehler wäre nicht aufgetreten, hätten sie das Formular benutzt.</p>";
exit;
}
// das große Fehlerarray.
$errors = array();
// das nicht-tödliche Warnungsarray
$warnings = array();
// mit allen Fehlern beenden
function exit_errors() {
global $ajax,$errors,$warnings;
print "<h2>Fehler in den angegeben Daten</h2>";
foreach($warnings as $error) {
print '<p class="big-warning">'.$error."</p>\n";
}
foreach($errors as $error) {
print '<p class="big-error">'.$error."</p>\n";
}
if($ajax) {
print "<p>Gehen sie zurück zum Formular und korrigieren sie ihre Fehleingaben.
Klicken sie dann nochmals auf <i>Generieren</i>.</p>";
print '<p><a href="javascript:history.go();">Zurück zum Formular</a></p>';
}
exit;
}
// Schritt 1: Eingaben entgegennehmen und validieren
// 1/6: Punch-Daten
$input_source = get('data-src');
switch($input_source) {
case 'file': // Dateiupload. Tja. Datei direkt dem Programm übergeben. Vorher
// Größe checken. Also mal los:
if($ajax) {
// wenn Ajax benutzt wird, wird die Datei noch nicht hochgeladen.
break; // also erst anschließend machbar.
}
if(!isset($_FILES['data-file'])) {
$errors[] = "Keine Datei hochgeladen, obwohl Dateiupload als Datenquelle ausgewählt war!";
} elseif($_FILES['data-file']['size'] > 500*1024) {
$errors[] = "Die hochgeladene Datei ist größer als ein 500kb. Derart große
Lochstreifen sind aus Perfomancegründen nicht erlaubt. Laden sie
sich das Visualisierungsprogramm runter und erstellen sie lokal
ihre Mammutlochstreifen.";
} elseif($_FILES['data-file']['size'] == 0) {
$warnings[] = "Die hochgeladene Datei ist leer (0 Bytes Inhalt). Das gibt keinen sonderlich
spannenden Lochstreifen...";
}
$original_filename = $_FILES['data-file']['name']; // fuer den Report weiter unten
$input_filename = $_FILES['data-file']['tmp_name'];
// usw.
break;
case 'input': // Daten direkt eingetippt. Na dann...
$text = get('data-input');
$form = get('data-form');
$input_binary = 'abc';
$parts = preg_split("/\s+|\n+/", $text);
foreach($parts as $x => $byte) {
if(!strlen($byte)) continue; // nur whitespace, Abfall durch das preg_split.
// Alle Formen zu dezimal umwandeln.
if($form == 'bin')
$byte = bindec($byte);
elseif($form == 'hex')
$byte = hexdec($byte);
elseif($form == 'oct')
$byte = octdec($byte);
elseif($form == 'hex0')
$byte = hexdec(substr($byte,2)); // 0x wegschneiden.
elseif($form != 'dec') {
$errors[] = "Fehler: Ungültige Datenform <i>$form</i>!";
break;
}
if(!is_numeric($byte)) {
$errors[] = "Fehler: Byte $x mit dem Wert <i>$byte</i> ist keine entsprechende
Zahl des Types <i>$form</i>. Vielleicht haben sie nicht das
passende Format eingestellt, in dem sie ihre Daten eingegeben haben?";
break;
}
$input_binary .= chr($byte); // <- the PHP way | the Perl way -> pack('C', $byte);
} // foreach
if(strlen($input_binary) > 1024*1024) {
$errors[] = 'Sie haben über eine Millionen Bytes eingetippt! Derart große
Lochstreifen sind aus Perfomancegründen nicht erlaubt. Laden sie
sich das Visualisieurngsprogramm runter und erstellen sie ihren
Monsterstreifen lokal.';
break;
} elseif(strlen($input_binary) == 0) {
$errors[] = 'Sie haben keine Daten eingegeben, obwohl direkte Byteeingabe als
Datenquelle ausgewählt wurde!';
break;
}
// so, jetzt liegts als $input_binary vor.
break;
case 'font': // Text, der gepuncht werden soll
$text = get('data-text');
if(!preg_match('/^[a-zA-Z0-9 ]+$/', $text)) {
$warnings[] = 'Im Text befinden sich nichtdruckbare Zeichen. Nur Buchstaben und Zahlen
können dargestellt werden, keine anderen Zeichen.';
}
if(strlen($text) == 0) {
$errors[] = 'Sie haben keinen Text eingegeben, obwohl Textgenerierung als Datenquelle
ausgewählt wurde!';
}
if(strlen($text) > 1000) {
$errors[] = 'Der Text ist mit über 1000 Zeichen eindeutig zu lang.
Aus Perfomancegründen ist das nicht erlaubt. Laden sie sich die
entsprechenden Programme runter und erstellen sie ihren Streifen lokal.';
}
// irgendwas mit dem Text machen.
// und als $input_binary fertig machen.
$input_binary = 'abc';
break;
default: $errors[] = "Ungültige Datenquelle! Es konnten keine Daten entgegengenommen werden!";
}
// 2/6: Formatfragen.
$format = strtolower(get('format'));
if($format != 'png' && $format != 'svg') {
$errors[] = "Ungültiges Ausgabeformat <i>$format</i>! Erlaubt sind nur PNG und SVG!";
}
// 3/6: Dimensionen
$dsrc = get('dimension-src');
if($dsrc == 'width' || $dsrc == 'height' || $dsrc == 'diameter') {
$size = get("dimension-$dsrc");
if(!my_ctype_digit($size)) {
$errors[] = "Die Größenangabe zu den Dimensionen, die der Lochstreifen annehmen soll,
ist fehlerhaft: $size ist keine positive ganze Dezimazahl!";
} elseif($dsrc == 'diameter' && $size > 100) {
$errors[] = "Der Durchmesser eines Loches ist mit 100 pxieln zu groß, ein so großes Bild
darf aus Perfomancegründen nicht generiert werden. Bitte kleineren Wert
einstellen.";
} elseif($size > 1000*100) {
$errors[] = "Die Größenangabe von über 100.000 Pixel ist viel zu groß. Bitte kleineren Wert
einstellen.";
}
// alles klar. $size und $dsrc
} else
$errors[] = "Es wurde keine Größenangabe zum Lochstreifen gemacht!";
// 4/6: Ausrichtung des Lochstreifens
$alignment = get('alignment');
if(preg_match('/^(hor)-(rtl|ltr)-([ou])$|^(ver)-(btt|ttb)-([lr])$/', $alignment, $m)) {
// so, jetzt mal zusammenpuzzeln...
} else
$errors[] = "Ungültige Ausrichtung des Lochstreifens: $alignment war nicht in der Auswahl!";
// 5/6: Den ganzen komponentenweiten Kram
$comp = array('imagebg', 'tapebg', 'punched', 'notpunched', 'feedholes');
$comp_visibility = array(); $comp_color = array();
$comp_translation = array('Bildhintergrund', 'Lochstreifen', 'Löcher', 'Nicht-Löcher', 'Führungslöcher');
foreach($comp as $x => $component) {
$comp_visibility[$x] = get("show-$component") ? 1 : 0;
$color = substr(get("color-$component"), 1); // das "#" wegstrippen
$lucency = get("lucency-$component");
if(!preg_match('/^[0-9a-fA-F]{6}$/', $color)) {
$errors[] = "Die Farbangabe <i>$color</i> für den Komponenten <i>$component</i> ist keine korrekte Hexadezimalfarbangabe.";
continue;
} elseif(!preg_match('/^[0-9a-fA-F]{2}$/', $lucency)) {
$errors[] = "Die Transparenzangabe <i>$lucency</i> für den Komponenten <i>$component</i> ist nicht korrekt.";
continue;
}
$comp_color[$x] = "#${color}${lucency}";
}
// 6/6: Empty-start, Empty-End
$empty_start = get('empty-start');
$empty_end = get('empty-end');
if(!my_ctype_digit($empty_start))
$errors[] = "Die Anzahl leerer Startbytes ist keine positive ganze Zahl ($empty_start)";
if(!my_ctype_digit($empty_end))
$errors[] = "Die Anzahl leerer Endbytes ist keine positive ganze Zahl ($empty_end)";
// fertig.
// Fehler ausgeben und beenden.
if(!empty($errors)) {
exit_errors();
}
// keine Fehler aufgetreten und in Ajax-Mode: "ok" zurückgeben
if($ajax) {
echo "ok";
exit;
}
// Wenn wir hier sind, gehts schon mal weiter.
echo '<h2>Lochstreifengenerator</h2>';
if(!empty($warnings)) {
// es gibt Warnungen...
echo '<p>Es sind Probleme aufgetaucht, die allerdings nicht wirklich schlimm sind, der
Lochstreifen wurde trotzdem generiert:</p>';
foreach($warnings as $error) {
print '<p class="big-warning">'.$error."</p>\n";
}
}
// so, Parameter zusammenfummeln.
$exec_params = '';
// 2/6: Format:
$exec_params .= "-f$format ";
// 6/6 Empty Bytes:
if($empty_start) $exec_params .= "--empty-start=$empty_start ";
if($empty_end) $exec_params .= "--empty-end=$empty_end ";
// 5/6 Komponenten
foreach($comp as $x => $component) {
if($comp_visibility[$x]) {
$exec_params .= "--color-$component=".$comp_color[$x].' ';
} else {
$exec_params .= "--hide-$component ";
}
}
// 3/6 Dimensionen
$exec_params .= "--$dsrc=$size";
// 4/6 Transformations and Rotations
$exec_params .= $transformation_string.' ';
// Output Filename erstellen
$source_range = array_merge(range('a','z'), range('0', '9'));
$output_hash = '';
foreach(array_rand($source_range, 10) as $x) { $output_hash .= $source_range[$x]; }
/* //alternativ sowas wie:
$tmpx = mt_rand(0,32); // tempoaer fuer nen kurzen hash
if($tmpx-32 < 10) $tmpx *= -1;
$output_hash = substr(md5(rand() * time()), $tmpx, $tmpx+10);*/
$image_filename = $output_dir.'/'.$output_hash.'.'.$format;
$exec_params .= "--output=$image_filename";
// 1/6 Input Data
if($input_source == 'file') {
$exec_params .= " '".$input_filename."'";
} else {
// Binäre Daten liegen als $input_binary vor und wollen
// per STDIN eingefüttert werden.
}
print "<p>Der Lochstreifen wird nun generiert...\n";
flush(); // damit er das wenigstens sieht...
if($verbose) {
print "<h2>Verbose-Ausgaben</h2>";
print "<p>Alles zusammengepanscht ergibt:</p>";
print "<tt>$draw_program $exec_params</tt>";
print "<p>Rufe auf...\n";
}
// in PHP brauchts nen mörderischen Aufwand, um jetzt dieses
// Programm anzuschmeißen:
$proc_ressource = proc_open("time $draw_program ".($verbose?'--verbose ':' ').$exec_params, array(
// descriptor array (0=stdin, 1=stdout, 2=stderr, like sh)
0 => array('pipe', 'r'), // Programm wird von STDIN lesen
1 => array('pipe', 'w'), // und zu STDOUT schreiben
2 => array('pipe', 'w'), // und zu STDERR auch schreiben
), $pipes, NULL , NULL );
if(is_resource($proc_ressource)) {
if($input_source != 'file') {
// Daten reinschreiben
fwrite($pipes[0], $input_binary);
}
fclose($pipes[0]); // nichts mehr zu schreiben
// STDOUT/STDERR lesen
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]); // fuer Speicherung der <times>-Ausgaben.
fclose($pipes[1]);
fclose($pipes[2]);
$return_value = proc_close($proc_ressource);
if($return_value) {
print "<p class='big-error'>Fehler: Generator wurde nicht ordnungsgemäß ausgeführt, das
Programm beendete mit <u>Statuscode $return_value</u>.</p>";
}
if($return_value || $verbose) {
if(!$verbose) {
print "<p>Folgende Ausgaben wurden zwecks Fehlersuche abgefangen:</p>\n";
print "<p>Aufruf: <tt>$draw_program $exec_params</tt>";
}
print "<pre>";
print "<b>STDOUT:</b>\n";
echo $stdout;
print "\n<b>STDERR:</b>\n";
echo $stderr;
print "</pre>";
print "\n<b>Return Value $return_value</b>\n";
}
// checken ob was gemacht wurde.
if(!is_file($image_filename)) {
print "<p class='big-error'>Gewünschtes Bild konnte nicht erstellt werden!</p>";
exit; // dann brauchts auch kein Report und so. Der braucht nämlich die Datei.
}
} else
print '<p class="big-error">Konnte Generatorprozess nicht starten!</p>';
// + Übersichtsseite machen, erst mal in String, und ausgeben.
$dir_url = dirname($_SERVER['PHP_SELF']);
$image_url = "$dir_url/$image_filename";
$report_file = $output_dir.'/'.$output_hash.'.htm';
$report_url = "$dir_url/$report_file";
$filesize = DataSizeFormat(filesize($image_filename));
$report = "<p>Der folgende Lochstreifen wurde am <b>".date('m.d.Y')." um ".date('H:i:s')." Uhr</b> von dem
Besucher mit der <b>IP-Adresse $_SERVER[REMOTE_ADDR]</b> generiert. Die erstellte Datei vom
Typ <b>".strtoupper($format)."</b> ist <b>$filesize</b> groß";
if($format == 'png') {
$imagesize = GetImageSize($image_filename);
$report .= ", sie hat die Ausmaße von <b>$imagesize[0] x $imagesize[1]</b> Pixel.</p>\n";
$report .= "<p>Das Bild wurde gespeichert unter der Adresse <b><a href='$image_url'>$image_url</a></b>
und kann z.B. zur Weiterverwendung runtergeladen werden. Es sieht so aus:</p>
<p><img src='$image_url' alt='Der generierte Lochstreifen' $imagesize[2]></p>";
} else {
$report .= ".</p><p>Das Bild wurde unter der Adresse <b><a href='$image_url'>$image_url</a></b> gespeichert.
Mit einem modernen Browser (Nicht Internet Explorer) können sie sich die SVG-Datei durch
Anklicken des Linkes anschauen. Laden sie sich (ansonten) die Datei runter und öffnen sie
es mit einem Vektorgrafikprogramm, z.B. mit dem frei erhältlichen <a href='www.inkscape.org'
>Inkscape</a>.</p>";
}
// Discmailer ;-)
$report .= "<p>Bitte beachten sie: Die Verfügbarkeit des Bildes auf diesem Server gehört nicht in den
\"Dienstleistungsumfang\" des Generierungsprogramms. Das heißt, dass sie <u>durch Eingeben ihrer
Daten zugestimmt haben, dass diese mit dem Bild nach freiem Ermessen des Administrators gespeichert
werden und willkürlich gelöscht werden können</u>. Es besteht kein Anspruch auf selbst zeitlich
begrenztes Hosting! Wenn sie das Bild im Internet verfügbar machen wollen, können sie es z.B.
auf kostenlosen Bilderhostern hochladen. Suchen sie dazu mit der Suchmaschine ihres Vertrauens
nach so etwas wie z.B. \"<a href='http://www.google.de/search?q=free+image+hosting'
>image hosting</a>\"</i></p>";
$report .= "<h3>Die Daten</h3>";
// Daten als HEX aufbereiten
if($input_source == 'file')
$input_binary = file_get_contents($input_filename);
$report_input_source = array(
'file' => "als eine Datei mit dem Namen <b>$original_filename</b> hochgeladen",
'input' => "<b>$form</b>-kodiert eingetippt",
'font' => "als zu generierende Schrift eingetippt, zugrunde lag der Text <b>$text</b>"
);
$report .= "<p>Die Daten, die dem generierten Lochstreifen zugrundeliegen, wurden vom Benutzer
$report_input_source[$input_source]. Im folgenden wird ein Hexdump der <b>".strlen($input_binary)."
Bytes</b> angegeben, mit denen letztendlich der Lochstreifen generiert wurde. Mit geeigneten
Hilfmitteln (z.B. einem Hexeditor) können sie aus den Daten eine Binärdatei erstellen.";
$report .= "<pre>";
for($x=0; $x<strlen($input_binary); $x++) {
$report .= sprintf("%02x ", ord($input_binary[$x]));
if((($x+1) % 20) == 0) {
$report .= "\n";
}
}
$report .= "</pre>";
// weitere Parameter abhandeln.
$report .= "<h3>Weitere Parameter</h3>";
$report .= "<p>Folgende Einstellungen/Farben liegen dem Lochstreifen zugrunde:</p><ul>";
foreach($comp as $x => $c) {
$report .= "<li><b>$comp_translation[$x]</b> ($c): ".
($comp_visibility[$x] ? "sichtbar, und zwar mit der Farbe <b>$comp_color[$x]</b> (Format #RRGGBBAA)"
: "nicht sichtbar (deaktiviert)");
}
$report .= "</ul>";
$report .= "<p>Außerdem wurden <b>$empty_start leere Startbytes und $empty_end leere Endbytes</b> eingestellt.
Die eingestellte Rotation des Lochstreifens wird durch das folgende Bild verdeutlicht:</p>
<img src='$dir_url/design/$alignment.png' alt='$alignment'>
<p>Die Größe des Lochstreifens wurde von der Dimension <b>$dsrc</b> abhängig gemacht und auf
<b>$size</b> eingestellt.</p>";
$report .= "<h3>Generierung</h3>";
$report .= "<p>Mit dem Kommandozeilentool führ(t)en folgende <b>Parameter</b> zu der Generierung eines derart
gewünschten Lochstreifens:
<br><tt>$exec_params</tt>
<br>Durch Eingabe dieser Parameter mit den oben angezeigten Eingabedaten müsste jederzeit
das gleiche Bild reproduzierbar sein.</p>";
$report .= "<!-- STDOUT:\n$stdout\n\nSTDERR:\n$stderr\n\n-->"; // debugging...
$report .= "<p>Das Generieren des Lochstreifens nahm die folgende <b>Zeit in Anspruch</b>:<pre>$stderr</pre>
<p>Dabei steht <i>real</i> für die wirklich vergangene Zeit, die der Benutzer warten musste,
bevor er seinen Lochstreifen bekam, <i>user</i> für die tatsächliche Zeit, die das Programm
aktiv war (Abweichungen zu <i>real</i> können dadurch begründet sein, dass der Server noch
andere Sachen gleichzeit machte) und <i>system</i> für die Zeit, in der der Server wirklich
ernsthaft über den Lochstreifen nachgedacht hat ;-)</p>";
//// REPORT zuende. Jetzt erst mal ausdrucken.
if(!$verbose) echo "fertig</p>\n"; // als Antwort auf die "wird nun generiert..."-Ausgaben weit oben.
echo "<p>Mit dem generierten Bild wurde eine Urkunde erstellt, in der alle Parameter, Daten und sonstige
interessante Dinge festgehalten sind. Es folgt nur dessen Inhalt:</p>";
echo "<div class='big-okay' style='font-weight:normal'>"; // ;)
echo $report;
echo "</div>";
echo "<p>Diese \"Urkunde\" wurde mit dem Bild zusammen gespeichert. Sie ist unter einer ähnlichen Adresse
wie das Bild erreichbar, und zwar unter <b><a href=\"$report_url\">$report_url</a></b>.</p>";
echo "<p>Möchten sie noch etwas stanzen? <a href='$dir_url'>Erneut zum Stanzereingabeformular</a></p>";
// und jetzt abspeichern!
$fh = fopen($report_file, 'w');
if(!$fh) {
print '<p class="big-error">Fehler: Konnte Report-File '.$report_file.' nicht zum Schreiben öffnen!</p>';
exit; // weil gibts ja nichts mehr zu tun.
}
fwrite($fh, makeHeader("Lochstreifen $output_hash -- Erstellungsbericht"));
fwrite($fh, "<h2>Lochstreifen $output_hash</h2>");
fwrite($fh, $report);
fwrite($fh, "</body></html>");
fclose($fh);
// so, jetzt aber wirklich fertig. :)
function DataSizeFormat($size,$ukb = "1024",$plain = "")
{
// immer wieder eine schöne Funktion, die man sich von seinen
// alten Scripten immer wieder neu kopiert ;-)
$kb = $ukb; // Kilobyte
$mb = $ukb * $kb; // Megabyte
$gb = $ukb * $mb; // Gigabyte
$tb = $ukb * $gb; // Terabyte
$sp = ' '; // No breaking space
if($size < $kb) {
$readable = $size;
$definition = $sp.'Byte';
}elseif($size < $mb) {
$readable = ($size/$kb);
$definition = $sp.'KB';
}elseif($size < $gb) {
$readable = ($size/$mb);
$definition = $sp.'MB';
}elseif($size < $tb) {
$readable = ($size/$gb);
$definition = $sp.'GB';
}else {
$readable = ($size/$tb);
$definition = $sp.'TB';
}
if ($plain == 1) {
$definition = "";
}
if ($size < $kb) {
if (empty($readable)) {
$readable = 0;
}
return $readable.$definition;
} else {
return number_format($readable,2).$definition;
}
}