Kategorien
FreewarWiki:Bot/Skripts/makemap.php: Unterschied zwischen den Versionen
< FreewarWiki:Bot | Skripts
Sphinx (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „{{Scriptquelltextverwendung}} <pre> <?php header('Content-Type: text/plain; charset=utf-8;'); // Bergfeld url define("BERGFELD", 'http://welt1.freewar.de/free…“) |
(Alles neu macht der Februar) |
||
(3 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
{{Scriptquelltextverwendung}} | {{Scriptquelltextverwendung}} | ||
Letzte Änderungen: | |||
* Umstellung auf "getopt" (Angabe von Optionen auf der Befehlszeile). Neue Features "Atlas" und "Einzelkarten", damit auch Erstellung von Dungeon-Karten. Damit kann man jetzt weitgehend automatisch sowas machen wie http://www.remote-island.org/101912/atlas.pdf (Quelltext: http://www.remote-island.org/101912/atlas.odt) --[[Benutzer:Count Ypsilon|Count Ypsilon]] ([[Benutzer Diskussion:Count Ypsilon|Diskussion]]) 23:24, 17. Feb. 2019 (CET) | |||
<pre> | <pre> | ||
<?php | <?php | ||
header('Content-Type: text/plain; charset=utf-8;'); | header('Content-Type: text/plain; charset=utf-8;'); | ||
const BERGFELD = 'http://welt1.freewar.de/freewar/images/map/std.jpg'; | |||
const ATLAS_TEMPLATE = 'atlas-vorlage.odt'; | |||
const GESAMTKARTE_LORU = array(2, 2, 170, 400); | |||
const MAPLIST = 'maplist.txt'; | |||
const MAPCACHE = './map_cache'; | |||
/* ---------------------------------------------------------------------------- | |||
* Der Freewar-Kartengenerator | |||
* | |||
* | |||
*/ | |||
function show_help() { | |||
echo <<<EOF | |||
Befehlszeilenoptionen: | |||
--mode "gesamt": erzeugt eine Gesamtkarte für die Oberfläche | |||
"einzel": erzeugt einzelne Karten für jedes Gebiet | |||
"atlas": erzeugt ein Atlas-Dokument (OpenOffice/Libre- | |||
Office-Format); benötigt dafür eine Vorlage | |||
--dungeons auch Dungeons mit ausgeben | |||
--grid alle X Zeilen/Spalten eine Linie | |||
--cellspacing Pixel Abstand zwischen Feldern | |||
--gridlabels Gitternetz mit Koordinaten beschriften | |||
--rotatelabels Koordinaten an X-Achse rotieren | |||
--gebietlabel Gebiete beschriften (bei Einzel/Atlas) | |||
--labelfont TTF-Datei für Beschriftungen | |||
--lighten nichtbetretbare/gebietsfremde Felder aufhellen | |||
--output Name der Ausgabedatei/des Ausgabeverzeichnisses | |||
--bgcolor Bildhintergrund in Hex | |||
--verbose mehr Meldungen anzeigen | |||
Bei Modus "Atlas" zusätzlich | |||
--dpi Auflösung für die Bildausgabe (Default 140, größere Auflösung | |||
bringt kleinere Bilder) | |||
--pagewidth druckbare Seitenbreite in cm | |||
--pageheight druckbare Seitenhöhe in cm | |||
EOF; | |||
} | |||
// --- Einlesen der Befehlszeile ---------------------------------------------- | |||
// | $longopts = array( | ||
$ | "mode:", // gesamt (Default), einzel oder atlas | ||
"dungeons", // sollen Dungeons mit ausgegeben werden? | |||
"verbose", // soll das Programm geschwätzig sein? | |||
"lighten", // sollen gebietsfremde Felde aufgehellt werden? | |||
"bgcolor:", // Kartenhintergrund in Web-Notation (Default weiss) | |||
"grid:", // Gitternetz-Abstand (Default keins) | |||
"help", // Hilfe anzeigen | |||
"gridlabels", // soll das Gitternetz beschriftet sein? (Default nein) | |||
"gebietlabel", // soll die Gebietskarte ein Label haben? (Default nein) | |||
"labelfont:", // TTF-Datei für die Beschriftungen | |||
"rotatelabels", // soll X-Achesen-Label 90° rotiert sein? (Default nein) | |||
"cellspacing:", // Feld-Abstand (Default 0) | |||
"dpi:", // dpi für Bilder im Atlas (Default 140) | |||
"pagewidth:", // druckbare Seitenbreite (cm, Papier-Rand) im Atlas | |||
"pageheight:", // druckbare Seitenhöhe (cm, Papier-Rand) im Atlas | |||
"output:", // Ausgabedatei oder -Verzeichnis | |||
); | |||
$o = getopt(NULL, $longopts, $optind); | |||
$o['verbose'] = array_key_exists('verbose', $o); | |||
if (array_key_exists('help', $o)) { | |||
show_help(); | |||
exit; | |||
} | |||
if (!array_key_exists('mode', $o)) { | |||
$ | $o['mode'] = 'gesamt'; | ||
echo "Betriebsmodus \"Gesamtkarte\" automatisch gewählt. Programm mit --help\n"; | |||
echo "aufrufen für weitere Funktionen\n"; | |||
} | |||
if ($o['mode'] != 'gesamt' && $o['mode'] != 'atlas' && $o['mode'] != 'einzel') { | |||
$ | echo "mode muss entweder 'atlas', 'einzel' oder 'gesamt' sein\n"; | ||
show_help(); | |||
exit; | |||
} | |||
if (!array_key_exists('bgcolor', $o)) { | |||
$o['bgcolor'] = 'ffffff'; | |||
} | |||
$ | |||
if (!preg_match('/^[a-f0-9]{6}$/i', $o['bgcolor'])) { | |||
$ | echo "bgcolor muss eine 6stellige Hexadezimalzahl sein, z.B. fafefa - nicht '".$o['bgcolor']."'\n"; | ||
show_help(); | |||
exit; | |||
} | |||
if (!array_key_exists('grid', $o)) { | |||
$o['grid'] = 0; | |||
$ | } | ||
if (!preg_match('/^\d\d?$/', $o['grid'])) { | |||
echo "grid muss zwischen 0 und 99 liegen\n"; | |||
show_help(); | |||
exit; | |||
} | |||
$o['gridlabels'] = array_key_exists('gridlabels', $o); | |||
if ($o['gridlabels'] && !$o['grid']) { | |||
echo "gridlabels kann nur zusammen mit grid verwendet werden\n"; | |||
show_help(); | |||
exit; | |||
} | |||
$o['rotatelabels'] = array_key_exists('rotatelabels', $o); | |||
if ($o['rotatelabels'] && !$o['gridlabels']) { | |||
echo "rotatelabels kann nur zusammen mit gridlabels verwendet werden\n"; | |||
show_help(); | |||
exit; | |||
} | |||
$o['dungeons'] = array_key_exists('dungeons', $o); | |||
$o['gebietlabel'] = array_key_exists('gebietlabel', $o); | |||
if ($o['gebietlabel'] && $o['mode'] == 'gesamt') { | |||
echo "--gebietlabel geht nur bei --mode atlas oder --mode einzel\n"; | |||
show_help(); | |||
exit; | |||
} | } | ||
// | $o['lighten'] = array_key_exists('lighten', $o); | ||
if ($o['lighten'] && $o['mode'] == 'gesamt') { | |||
$ | echo "--lighten geht nur bei --mode atlas oder --mode einzel\n"; | ||
if (! | show_help(); | ||
exit; | |||
} | |||
if (!array_key_exists('cellspacing', $o)) $o['cellspacing'] = 0; | |||
if (!preg_match('/^\d\d?$/', $o['cellspacing'])) { | |||
echo "cellspacing muss zwischen 0 und 99 liegen\n"; | |||
show_help(); | |||
exit; | |||
} | |||
if ($o['mode'] == 'atlas') { | |||
if (!array_key_exists('dpi', $o)) { | |||
$o['dpi'] = 140; | |||
} | |||
if (!preg_match('/^\d\d\d?$/', $o['dpi'])) { | |||
echo "dpi muss zwischen 10 und 999 liegen\n"; | |||
show_help(); | |||
exit; | |||
} | |||
if (!array_key_exists('pagewidth', $o)) $o['pagewidth'] = 12.85; | |||
$o['pagewidth'] = strtr($o['pagewidth'], ",", "."); | |||
if (!array_key_exists('pageheight', $o)) $o['pageheight'] = 19; | |||
$o['pageheight'] = strtr($o['pageheight'], ",", "."); | |||
if (!preg_match('/^\d\d?(\.\d+)?$/', $o['pageheight'])) { | |||
echo "pageheight muss zwischen 0 und 99.9 liegen\n"; | |||
show_help(); | |||
exit; | |||
} | |||
if (!preg_match('/^\d\d?(\.\d+)?$/', $o['pagewidth'])) { | |||
echo "pagewidth muss zwischen 0 und 99.9 liegen\n"; | |||
show_help(); | |||
exit; | |||
} | |||
} else { | |||
if (array_key_exists('dpi', $o) || | |||
array_key_exists('pageheight', $o) || | |||
array_key_exists('pagewidth', $o)) { | |||
echo "--dpi, --pageheight, --pagewidth gehen nur mit --mode atlas\n"; | |||
show_help(); | |||
exit; | |||
} | } | ||
} | } | ||
$ | if (!array_key_exists("labelfont", $o)) { | ||
$ | $o['labelfont'] = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'; | ||
} | |||
if ($o['gebietlabel'] || $o['gridlabels']) { | |||
if (!file_exists($o['labelfont'])) { | |||
echo "Datei ".$o['labelfont']." (--labelfont) nicht gefunden\n"; | |||
show_help(); | |||
exit; | |||
} | |||
} | |||
// | if (!array_key_exists("output", $o)) { | ||
$ | $o['output'] = './karten'; | ||
if ($o['mode'] == 'atlas') $o['output'] = './atlas.odt'; | |||
if ($o['mode'] == 'gesamt' && !$o['dungeons']) $o['output'] = './Gesamtkarte (automatisch generiert).jpg'; | |||
} | |||
if ($optind < $argc) { | |||
echo "Ungültige Befehlszeilenoption\n"; | |||
show_help(); | |||
exit; | |||
} | |||
// | // --- Einlesen der Befehlszeile beendet -------------------------------------- | ||
if ($o['mode'] == 'atlas') { | |||
// | // Atlas benötigt eine vorbereitete OpenOffice-Datei | ||
if (!file_exists(ATLAS_TEMPLATE)) { | |||
echo "Datei ".ATLAS_TEMPLATE." wird für --mode atlas benötigt, fehlt aber.\n"; | |||
echo "Entweder eine leere OpenOffice-Datei erzeugen, die irgendwo den Text\n"; | |||
echo "ADD CONTENT HERE enthält, oder herunterladen von\n"; | |||
echo "http://www.remote-island.org/101912/atlas-vorlage.odt"; | |||
exit; | |||
} | |||
if ($o['verbose']) echo ATLAS_TEMPLATE.' nach '.$o['output']." kopieren...\n"; | |||
$ | copy(ATLAS_TEMPLATE, $o['output']); | ||
$zipfile = new ZipArchive; | |||
$res = $zipfile->open($o['output']); | |||
$ | if (!$res) { | ||
echo "Fehler beim Öffnen der Datei ".$o['output']."!\n"; | |||
exit; | |||
} | |||
} else if ($o['mode'] != 'gesamt' || $o['dungeons']) { | |||
mkdir($o['output'], 0777, TRUE); | |||
if (!is_dir($o['output'])) { | |||
echo "'".$o['output']."' ist kein Verzeichnis bzw. kann nicht angelegt werden!\n"; | |||
exit; | |||
if ( | |||
} | } | ||
} | } | ||
$count = 10000; | |||
$ | |||
// ab hier nichts ändern ohne Kenntnisse über Funktionsweise des Skripts | |||
// cacht mapfile | |||
$ | function cache_mapfile($url) { | ||
$cache_file = MAPCACHE . "/". md5($url); | |||
$ | if (!file_exists($cache_file)) { | ||
file_put_contents($cache_file, file_get_contents($url)); | |||
} | |||
return $cache_file; | |||
} | } | ||
// | // trennzeichen für positionsschlüssel | ||
// | $pos_delimiter = '|'; | ||
$ | |||
// Bergfeld cachen | |||
cache_mapfile(BERGFELD); | |||
// maplist holen | |||
if ($o['verbose']) echo "Felder aus ".MAPLIST." holen und Bilder laden...\n"; | |||
$handle = fopen(MAPLIST, "r"); | |||
if (!$handle) { | |||
echo "Kann Feldliste aus ".MAPLIST." nicht laden\n"; | |||
exit; | |||
} | |||
$field_rows = array(); | |||
while ($data = fgetcsv($handle, 1000, ";")) { | |||
array_push($field_rows, $data); | |||
} | |||
fclose($handle); | |||
// felder in `position` => `url` format überführen | // felder in `position` => `url` format überführen | ||
$fields = []; | $fields = []; | ||
foreach ($field_rows as $field) { | foreach ($field_rows as $field) { | ||
$x = array_key_exists(2, $field) ? $field[2] : NULL; | |||
$y = array_key_exists(3, $field) ? $field[3] : NULL; | |||
continue; | $g = array_key_exists(0, $field) ? $field[0] : NULL; | ||
$b = array_key_exists(1, $field) ? $field[1] : NULL; | |||
if ($o["mode"] == "gesamt") { | |||
// Für die Gesamtkarte werden die Felder des Kontinents als ein Gebiet | |||
// behandelt | |||
if ($x>GESAMTKARTE_LORU[0] && $y>GESAMTKARTE_LORU[1] && | |||
$x<GESAMTKARTE_LORU[2] && $y<GESAMTKARTE_LORU[3]) { | |||
$g="Oberfläche"; | |||
} else { | |||
// Dungeons (und Außenbereiche des Kontinents wie Narubia) | |||
// nur, wenn --dungeons gesetzt ist | |||
if (!$o['dungeons']) continue; | |||
} | |||
} else { | |||
// Für Einzelkarten und Atlas kann man mit $dungeons steuern, | |||
// ob auch Dungeons ausgegeben werden sollen | |||
if (!$o['dungeons'] && $x<2) continue; | |||
} | } | ||
if ($ | |||
$ | $lim = &$gebiet_limits[$g]; | ||
if (!$lim) { | |||
$lim["minx"] = 99999; | |||
$lim["miny"] = 99999; | |||
$lim["maxx"] = -99999; | |||
$lim["maxy"] = -99999; | |||
} | } | ||
if ($ | |||
$ | // Hier wird die Größe jedes Gebiets ermittelt; dazu berücksichtigen | ||
// wir aber nur die begehbaren Teile, sonst haben Karten wie z.B. | |||
// Düsterfrostinsel einen unnötig großen Platzbedarf. TODO, dies | |||
$ | // konfigurierbar machen | ||
if ($b) { | |||
if ($x < $lim["minx"]) $lim["minx"] = $x; | |||
if ($y < $lim["miny"]) $lim["miny"] = $y; | |||
if ($x > $lim["maxx"]) $lim["maxx"] = $x; | |||
if ($y > $lim["maxy"]) $lim["maxy"] = $y; | |||
} | } | ||
// und gleich bilddatein holen | // und gleich bilddatein holen | ||
$cache_file = cache_mapfile($field[5] | $cache_file = cache_mapfile(array_key_exists(5, $field) ? $field[5] : NULL); | ||
// kein Feldtitel oder `Feldtitel Pensal (brennend)` | // kein Feldtitel oder `Feldtitel Pensal (brennend)` | ||
if (!isset($ | if (!isset($g) || strpos($g, ' (brennend)') === false) { | ||
$fields["{$ | $fields["{$x}$pos_delimiter{$y}"] = [ | ||
'x' => $ | 'x' => $x, | ||
'y' => $ | 'y' => $y, | ||
'g' => $g, | |||
'begehbar' => $b, | |||
'file' => $cache_file | 'file' => $cache_file | ||
]; | ]; | ||
Zeile 181: | Zeile 353: | ||
'x' => $field['x'] + $diff_x, | 'x' => $field['x'] + $diff_x, | ||
'y' => $field['y'] + $diff_y, | 'y' => $field['y'] + $diff_y, | ||
'file' => | 'file' => MAPCACHE . '/' . md5(BERGFELD) | ||
]; | ]; | ||
} | } | ||
Zeile 190: | Zeile 362: | ||
# Groesse eines Kartenfelds feststellen | # Groesse eines Kartenfelds feststellen | ||
list($tilewidth, $tileheight) = getimagesize($cache_file); | list($tilewidth, $tileheight) = getimagesize($cache_file); | ||
echo "tile size: $tilewidth x $tileheight\n"; | if ($o['verbose']) echo "tile size: $tilewidth x $tileheight\n"; | ||
// ein weisses Kartenfeld herstellen | |||
if ($o['lighten']) { | |||
$aufheller = imagecreatetruecolor($tilewidth, $tileheight); | |||
$farbe = imagecolorallocate($aufheller, 255, 255, 255); | |||
imagefilledrectangle($aufheller, 0, 0, $tilewidth, $tileheight, $farbe); | |||
} | |||
if ($o["mode"] == 'atlas') | |||
{ | |||
file_put_contents("odt/content.xml", file_get_contents("head.xml")); | |||
$dungeons = ""; | |||
$oberflaeche = ""; | |||
} | |||
foreach($gebiet_limits as $gebiet => $limits) { | |||
// Bereich ermitteln | |||
$max_x_found = $limits['maxx'] + 1; | |||
$max_y_found = $limits['maxy'] + 1; | |||
$min_x_found = $limits['minx'] - 1; | |||
$min_y_found = $limits['miny'] - 1; | |||
// Leeres Kartenbild erstellen | |||
$mapwidth = ($max_x_found - $min_x_found + ($o['grid'] ? 3 : 1)) * $tilewidth + | |||
($max_x_found - $min_x_found + 2) * $o['cellspacing']; | |||
$mapheight = ($max_y_found - $min_y_found + ($o['grid'] ? 3 : 1)) * $tileheight + | |||
($max_y_found - $min_y_found + 2) * $o['cellspacing']; | |||
if ($o['verbose']) { | |||
echo "Karte für '$gebiet': x_min: $min_x_found; x_max: $max_x_found; y_min: $min_y_found; y_max: $max_y_found; "; | |||
echo "Bildgröße: $mapwidth x $mapheight\n"; | |||
} | |||
$mapimage = imagecreatetruecolor($mapwidth, $mapheight); | |||
if (!$mapimage) { | |||
echo "Fehler bei der Erstellung des Kartenbilds für '$gebiet'\n"; | |||
continue; | |||
} | |||
// Hintergrundfarbe setzen | |||
$bgarray = sscanf($o['bgcolor'], "%02X%02X%02X"); | |||
$bgindex = imagecolorallocate($mapimage, $bgarray[0], $bgarray[1], $bgarray[2]); | |||
imagefill($mapimage, 0, 0, $bgindex); | |||
// Gitternetz einzeichnen und ggf. beschriften | |||
if ($o['grid']) { | |||
for ($x = $min_x_found; $x <= $max_x_found; $x++) { | |||
if ($x%$o['grid'] == 0) { | |||
$mpx = ($x - $min_x_found + 1) * ($tilewidth + $o['cellspacing']) + $o['cellspacing'] + $tilewidth/2; | |||
imageline($mapimage, $mpx, $o['rotatelabels'] ? 0 : 40, $mpx, $o['rotatelabels'] ? $mapheight : $mapheight - 40, 0); | |||
if (!$o['gridlabels']) continue; | |||
$t = $x; | |||
if (strlen($t) > 4) $t=substr($t,0,2)."\n".substr($t,3); | |||
// X-Achsen-Labels können entweder gerade stehen oder | |||
// entlang der Achse (rotatelabels) | |||
if ($o['rotatelabels']) { | |||
imagettftext($mapimage, 11, 270, $mpx + 3, 5, 0, $o['labelfont'], $t); | |||
$ar = imagettfbbox (11, 270, $o['labelfont'], $t); | |||
imagettftext($mapimage, 11, 270, $mpx + 3, $mapheight - $ar[3] - 5, 0, $o['labelfont'], $t); | |||
} else { | |||
$ar = imagettfbbox (11, 0, $o['labelfont'], $t); | |||
imagettftext($mapimage, 11, 0, $mpx + 3 - $ar[4]/2, 22 - $ar[5], 0, $o['labelfont'], $t); | |||
imagettftext($mapimage, 11, 0, $mpx + 3 - $ar[4]/2, $mapheight + $ar[5] - 10, 0, $o['labelfont'], $t); | |||
} | |||
} | |||
} | |||
for ($y = $min_y_found; $y <= $max_y_found; $y++) { | |||
if ($y%$o['grid'] == 0) { | |||
$mpy = ($y - $min_y_found + 1) * ($tileheight + $o['cellspacing']) + $o['cellspacing'] + $tileheight/2; | |||
imageline($mapimage, 0, $mpy, $mapwidth, $mpy, 0); | |||
if (!$o['gridlabels']) continue; | |||
$t = $y; | |||
if (strlen($t) > 4) $t=substr($t,0,2)."\n".substr($t,3); | |||
imagettftext($mapimage, 11, 0, 5, $mpy - 2, 0, $o['labelfont'], $t); | |||
$ar = imagettfbbox (11, 0, $o['labelfont'], $t); | |||
imagettftext($mapimage, 11, 0, $mapwidth - 5 - $ar[4], $mpy - 2, 0, $o['labelfont'], $t); | |||
} | |||
} | |||
} | |||
// Label für Gebiet einzeichnen; schwarze Box, darin weisse Box, | |||
// darin Text | |||
if ($o['gebietlabel']) { | |||
$ar = imagettfbbox (16, 0, $o['labelfont'], $gebiet); | |||
// FIXME die Zahlen hier sind etwas magisch durch Ausprobieren | |||
// gewählt | |||
imagefilledrectangle($mapimage, $mapwidth - $ar[4] - 55, | |||
$mapheight + $ar[7] - 26, $mapwidth-51, $mapheight-21, 0); | |||
imagefilledrectangle($mapimage, $mapwidth - $ar[4] - 54, | |||
$mapheight + $ar[7] - 25, $mapwidth-52, $mapheight-22, $bgindex); | |||
imagettftext($mapimage, 16, 0, $mapwidth - $ar[4] - 52, | |||
$mapheight + $ar[7] - 7, 0, $o['labelfont'], $gebiet); | |||
} | |||
// Feldbilder an die richtige Stelle im Gebiet einzeichnen. | |||
// Diese Schleife geht alle Felder durch, die im Rechteck liegen, das | |||
// das Gebiet umgibt. | |||
foreach ($fields as $key => $data) { | |||
// weiter, wenn das Feld ausserhalb ist | |||
if ($data['x'] < $min_x_found) continue; | |||
if ($data['x'] > $max_x_found) continue; | |||
if ($data['y'] < $min_y_found) continue; | |||
if ($data['y'] > $max_y_found) continue; | |||
// Feld kopieren | |||
$offset = ($o['grid']) ? 1 : 0; | |||
imagecopy($mapimage, | |||
imagecreatefromjpeg($data['file']), | |||
($data['x'] - $min_x_found + $offset) * | |||
($tilewidth + $o['cellspacing']) + $o['cellspacing'], | |||
($data['y'] - $min_y_found + $offset) * | |||
($tileheight + $o['cellspacing']) + $o['cellspacing'], | |||
0, 0, | |||
$tilewidth, $tileheight); | |||
// Feld aufhellen, wenn es nicht zum Gebiet gehört | |||
if ($o['lighten'] && (!array_key_exists('begehbar', $data) || !$data['begehbar'] || $gebiet != $data['g'])) { | |||
imagecopymerge($mapimage, | |||
$aufheller, | |||
($data['x'] - $min_x_found + $offset) * | |||
($tilewidth + $o['cellspacing']) + $o['cellspacing'], | |||
($data['y'] - $min_y_found + $offset) * | |||
($tileheight + $o['cellspacing']) + $o['cellspacing'], | |||
0, 0, | |||
$tilewidth, $tileheight, | |||
50); | |||
} | |||
} | |||
// Ausgabe des Bildes für ein Gebiet. Im Gesamtkartenmodus gibt es nur | |||
// ein Gebiet, also auch nur eine Ausgabe; in den anderen Modi passiert | |||
// das hier öfter. | |||
if ($o["mode"] == 'atlas') { | |||
// Für die Atlas-Ausgabe werden Bilder, die nicht auf die Seite | |||
// passen, eventuell gedreht oder zweigeteilt oder beides. Mehr | |||
// ist aber nicht drin - wenn das Bild auch gedreht und zweigeteilt | |||
// nicht passt, fliegt es raus. | |||
$px_per_cm = $o['dpi'] / 2.54; | |||
// Bildgröße in cm | |||
$w = $mapwidth / $px_per_cm; | |||
$h = $mapheight / $px_per_cm; | |||
// Bild-Pixel-Spalte, an der wir durchschneiden, wenn nötig | |||
// (wird gerundet auf ganze Felder, damit wir nicht mitten in | |||
// einem Feld schneiden) | |||
$cutoff = floor($o['pagewidth'] * $px_per_cm / ($tilewidth + $o['cellspacing'])) | |||
* ($tilewidth + $o['cellspacing']) - $o['cellspacing']/2 + 2; | |||
// Leeres Array initialisieren; eventuell haben wir mehr als | |||
// ein Ausgabebild | |||
$images = array(); | |||
if ($w <= $o['pagewidth'] && $h <= $o['pageheight']) { | |||
// alles super, Bild passt auf Seite | |||
array_push($images, $mapimage); | |||
($ | |||
$mapimage | |||
} elseif ($h <= $o['pagewidth'] && $w <= $o['pageheight']) { | |||
// Bild passt, wenn wir es drehen | |||
array_push($images, imagerotate($mapimage, 90, $bgindex)); | |||
imagedestroy($mapimage); | |||
} elseif ($w <= 2* $o['pagewidth'] && $h <= $o['pageheight']) { | |||
($ | |||
// Bild passt, wenn wir es durchschneiden - also schneiden | |||
// und zwei Teile in Ausgabe-Array stecken | |||
// TODO: Formel 2*pagewidth nicht ganz korrekt, da cutoff | |||
// bedeutet, dass wir die Seite nicht 100% ausnutzen | |||
array_push($images, imagecrop($mapimage, | |||
[ 'x' => 0, 'y' => 0, 'height' => $mapheight, 'width' => $cutoff ])); | |||
array_push($images, imagecrop($mapimage, | |||
[ 'x' => $cutoff, 'y' => 0, 'height' => $mapheight, | |||
'width' => $mapwidth - $cutoff])); | |||
imagedestroy($mapimage); | |||
} elseif ($w <= $o['pageheight'] && $h <= 2 * $o['pagewidth']) { | |||
// Bild passt rotiert und geschnitten | |||
$m = imagerotate($mapimage, 90, $bgindex); | |||
imagedestroy($mapimage); | |||
$mapimage = $m; | |||
$temp = $w; $w = $h; $h = $temp; | |||
array_push($images, imagecrop($m, [ 'x' => 0, 'y' => 0, | |||
'height' => $mapwidth, 'width' => $cutoff ])); | |||
array_push($images, imagecrop($m, [ 'x' => $cutoff, 'y' => 0, | |||
'height' => $mapwidth, 'width' => $mapheight - $cutoff])); | |||
imagedestroy($m); | |||
} else { | |||
echo "Bild für '$gebiet' zu groß. Wähle höhere DPI oder größeres Papier\n"; | |||
} | |||
// Wir führen zwei Ausgabedokumente, eins mit den oberirdischen | |||
// Gebieten und eins mit den Dungeons: | |||
if ($min_x_found < 0) { | |||
$xml = & $dungeons; | |||
} else { | |||
$xml = & $oberflaeche; | |||
} | |||
// OpenOffice-XML an das passende Dokument anfügen | |||
$xml .= '<text:h text:style-name="Heading_20_2" text:outline-level="2">'. | |||
$gebiet."</text:h>"; | |||
foreach ($images as $image) { | |||
$count++; | |||
// TODO: Dieser Dateiname muss eigentlich eine Prüfsumme des Inhalts | |||
// sein. Ist er es nicht, meldet LibreOffice immer ein "defektes | |||
// Dokument", bietet aber eine Reparatur an. | |||
ob_start(); | |||
imagepng($image); | |||
$imagestring = ob_get_clean(); | |||
$imagename = sprintf("Pictures/10000000%08X%08XDEADBEEF%08X.png", | |||
imagesx($image), imagesy($image), $count); | |||
$zipfile->addFromString($imagename, $imagestring); | |||
$h = imagesy($image) / $px_per_cm; | |||
$w = imagesx($image) / $px_per_cm; | |||
$xml .= <<<EOF | |||
<text:p text:style-name="P1"> | |||
<draw:frame draw:style-name="fr1" draw:name="Image$count" text:anchor-type="as-char" svg:width="${w}cm" svg:height="${h}cm" draw:z-index="0"> | |||
<draw:image xlink:href="$imagename" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" loext:mime-type="image/png"/> | |||
</draw:frame> | |||
</text:p> | |||
EOF; | |||
imagedestroy($image); | |||
} | |||
} elseif ($o['mode'] == 'einzel' || ($o['mode'] == 'gesamt' && $o['dungeons'])) { | |||
// Bei Einzelbildausgabe einfach PNG schreiben | |||
imagepng($mapimage, $o['output']."/".$gebiet.".png"); | |||
imagedestroy($mapimage); | |||
} else { | |||
// Bei Gesamtbild traditionell JPG | |||
// TODO was wenn ein .png als Ausgabename gewählt wurde? | |||
imagejpeg($mapimage, $o['output']); | |||
imagedestroy($mapimage); | |||
} | |||
} | |||
if ($o["mode"] == 'atlas') | |||
{ | |||
$old_contents = $zipfile->getFromName('content.xml'); | |||
if (!$old_contents) { | |||
echo ATLAS_TEMPLATE." enthält keine Datei content.xml - kein .odt-Dokument?\n"; | |||
} | |||
if (!preg_match('/(.*)<text:p[^<]*<text:span[^<]*ADD_CONTENT_HERE[^<]*<\/text:span>[^<]*<\/text:p>(.*)/', $old_contents, $matches)) { | |||
if (!preg_match('/(.*)<text:p[^<]*ADD CONTENT HERE[^<]*<\/text:p>(.*)/', $old_contents, $matches)) { | |||
echo "Die contents.xml im Atlas-Template entspricht nicht der erwarteten Form.\n"; | |||
echo $old_contents; | |||
exit; | |||
} | |||
} | |||
$contents = $matches[1] . $oberflaeche . $dungeons . $matches[2]; | |||
$zipfile->addFromString('content.xml', $contents); | |||
$zipfile->close(); | |||
echo "Atlas erstellt. Die fertige Datei muss nun im OpenOffice/LibreOffice\n"; | |||
echo "geöffnet und \"repariert\" werden.\n"; | |||
} | } | ||
</pre> | </pre> |
Aktuelle Version vom 18. Februar 2019, 00:24 Uhr
Letzte Änderungen:
- Umstellung auf "getopt" (Angabe von Optionen auf der Befehlszeile). Neue Features "Atlas" und "Einzelkarten", damit auch Erstellung von Dungeon-Karten. Damit kann man jetzt weitgehend automatisch sowas machen wie http://www.remote-island.org/101912/atlas.pdf (Quelltext: http://www.remote-island.org/101912/atlas.odt) --Count Ypsilon (Diskussion) 23:24, 17. Feb. 2019 (CET)
<?php header('Content-Type: text/plain; charset=utf-8;'); const BERGFELD = 'http://welt1.freewar.de/freewar/images/map/std.jpg'; const ATLAS_TEMPLATE = 'atlas-vorlage.odt'; const GESAMTKARTE_LORU = array(2, 2, 170, 400); const MAPLIST = 'maplist.txt'; const MAPCACHE = './map_cache'; /* ---------------------------------------------------------------------------- * Der Freewar-Kartengenerator * * */ function show_help() { echo <<<EOF Befehlszeilenoptionen: --mode "gesamt": erzeugt eine Gesamtkarte für die Oberfläche "einzel": erzeugt einzelne Karten für jedes Gebiet "atlas": erzeugt ein Atlas-Dokument (OpenOffice/Libre- Office-Format); benötigt dafür eine Vorlage --dungeons auch Dungeons mit ausgeben --grid alle X Zeilen/Spalten eine Linie --cellspacing Pixel Abstand zwischen Feldern --gridlabels Gitternetz mit Koordinaten beschriften --rotatelabels Koordinaten an X-Achse rotieren --gebietlabel Gebiete beschriften (bei Einzel/Atlas) --labelfont TTF-Datei für Beschriftungen --lighten nichtbetretbare/gebietsfremde Felder aufhellen --output Name der Ausgabedatei/des Ausgabeverzeichnisses --bgcolor Bildhintergrund in Hex --verbose mehr Meldungen anzeigen Bei Modus "Atlas" zusätzlich --dpi Auflösung für die Bildausgabe (Default 140, größere Auflösung bringt kleinere Bilder) --pagewidth druckbare Seitenbreite in cm --pageheight druckbare Seitenhöhe in cm EOF; } // --- Einlesen der Befehlszeile ---------------------------------------------- $longopts = array( "mode:", // gesamt (Default), einzel oder atlas "dungeons", // sollen Dungeons mit ausgegeben werden? "verbose", // soll das Programm geschwätzig sein? "lighten", // sollen gebietsfremde Felde aufgehellt werden? "bgcolor:", // Kartenhintergrund in Web-Notation (Default weiss) "grid:", // Gitternetz-Abstand (Default keins) "help", // Hilfe anzeigen "gridlabels", // soll das Gitternetz beschriftet sein? (Default nein) "gebietlabel", // soll die Gebietskarte ein Label haben? (Default nein) "labelfont:", // TTF-Datei für die Beschriftungen "rotatelabels", // soll X-Achesen-Label 90° rotiert sein? (Default nein) "cellspacing:", // Feld-Abstand (Default 0) "dpi:", // dpi für Bilder im Atlas (Default 140) "pagewidth:", // druckbare Seitenbreite (cm, Papier-Rand) im Atlas "pageheight:", // druckbare Seitenhöhe (cm, Papier-Rand) im Atlas "output:", // Ausgabedatei oder -Verzeichnis ); $o = getopt(NULL, $longopts, $optind); $o['verbose'] = array_key_exists('verbose', $o); if (array_key_exists('help', $o)) { show_help(); exit; } if (!array_key_exists('mode', $o)) { $o['mode'] = 'gesamt'; echo "Betriebsmodus \"Gesamtkarte\" automatisch gewählt. Programm mit --help\n"; echo "aufrufen für weitere Funktionen\n"; } if ($o['mode'] != 'gesamt' && $o['mode'] != 'atlas' && $o['mode'] != 'einzel') { echo "mode muss entweder 'atlas', 'einzel' oder 'gesamt' sein\n"; show_help(); exit; } if (!array_key_exists('bgcolor', $o)) { $o['bgcolor'] = 'ffffff'; } if (!preg_match('/^[a-f0-9]{6}$/i', $o['bgcolor'])) { echo "bgcolor muss eine 6stellige Hexadezimalzahl sein, z.B. fafefa - nicht '".$o['bgcolor']."'\n"; show_help(); exit; } if (!array_key_exists('grid', $o)) { $o['grid'] = 0; } if (!preg_match('/^\d\d?$/', $o['grid'])) { echo "grid muss zwischen 0 und 99 liegen\n"; show_help(); exit; } $o['gridlabels'] = array_key_exists('gridlabels', $o); if ($o['gridlabels'] && !$o['grid']) { echo "gridlabels kann nur zusammen mit grid verwendet werden\n"; show_help(); exit; } $o['rotatelabels'] = array_key_exists('rotatelabels', $o); if ($o['rotatelabels'] && !$o['gridlabels']) { echo "rotatelabels kann nur zusammen mit gridlabels verwendet werden\n"; show_help(); exit; } $o['dungeons'] = array_key_exists('dungeons', $o); $o['gebietlabel'] = array_key_exists('gebietlabel', $o); if ($o['gebietlabel'] && $o['mode'] == 'gesamt') { echo "--gebietlabel geht nur bei --mode atlas oder --mode einzel\n"; show_help(); exit; } $o['lighten'] = array_key_exists('lighten', $o); if ($o['lighten'] && $o['mode'] == 'gesamt') { echo "--lighten geht nur bei --mode atlas oder --mode einzel\n"; show_help(); exit; } if (!array_key_exists('cellspacing', $o)) $o['cellspacing'] = 0; if (!preg_match('/^\d\d?$/', $o['cellspacing'])) { echo "cellspacing muss zwischen 0 und 99 liegen\n"; show_help(); exit; } if ($o['mode'] == 'atlas') { if (!array_key_exists('dpi', $o)) { $o['dpi'] = 140; } if (!preg_match('/^\d\d\d?$/', $o['dpi'])) { echo "dpi muss zwischen 10 und 999 liegen\n"; show_help(); exit; } if (!array_key_exists('pagewidth', $o)) $o['pagewidth'] = 12.85; $o['pagewidth'] = strtr($o['pagewidth'], ",", "."); if (!array_key_exists('pageheight', $o)) $o['pageheight'] = 19; $o['pageheight'] = strtr($o['pageheight'], ",", "."); if (!preg_match('/^\d\d?(\.\d+)?$/', $o['pageheight'])) { echo "pageheight muss zwischen 0 und 99.9 liegen\n"; show_help(); exit; } if (!preg_match('/^\d\d?(\.\d+)?$/', $o['pagewidth'])) { echo "pagewidth muss zwischen 0 und 99.9 liegen\n"; show_help(); exit; } } else { if (array_key_exists('dpi', $o) || array_key_exists('pageheight', $o) || array_key_exists('pagewidth', $o)) { echo "--dpi, --pageheight, --pagewidth gehen nur mit --mode atlas\n"; show_help(); exit; } } if (!array_key_exists("labelfont", $o)) { $o['labelfont'] = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'; } if ($o['gebietlabel'] || $o['gridlabels']) { if (!file_exists($o['labelfont'])) { echo "Datei ".$o['labelfont']." (--labelfont) nicht gefunden\n"; show_help(); exit; } } if (!array_key_exists("output", $o)) { $o['output'] = './karten'; if ($o['mode'] == 'atlas') $o['output'] = './atlas.odt'; if ($o['mode'] == 'gesamt' && !$o['dungeons']) $o['output'] = './Gesamtkarte (automatisch generiert).jpg'; } if ($optind < $argc) { echo "Ungültige Befehlszeilenoption\n"; show_help(); exit; } // --- Einlesen der Befehlszeile beendet -------------------------------------- if ($o['mode'] == 'atlas') { // Atlas benötigt eine vorbereitete OpenOffice-Datei if (!file_exists(ATLAS_TEMPLATE)) { echo "Datei ".ATLAS_TEMPLATE." wird für --mode atlas benötigt, fehlt aber.\n"; echo "Entweder eine leere OpenOffice-Datei erzeugen, die irgendwo den Text\n"; echo "ADD CONTENT HERE enthält, oder herunterladen von\n"; echo "http://www.remote-island.org/101912/atlas-vorlage.odt"; exit; } if ($o['verbose']) echo ATLAS_TEMPLATE.' nach '.$o['output']." kopieren...\n"; copy(ATLAS_TEMPLATE, $o['output']); $zipfile = new ZipArchive; $res = $zipfile->open($o['output']); if (!$res) { echo "Fehler beim Öffnen der Datei ".$o['output']."!\n"; exit; } } else if ($o['mode'] != 'gesamt' || $o['dungeons']) { mkdir($o['output'], 0777, TRUE); if (!is_dir($o['output'])) { echo "'".$o['output']."' ist kein Verzeichnis bzw. kann nicht angelegt werden!\n"; exit; } } $count = 10000; // ab hier nichts ändern ohne Kenntnisse über Funktionsweise des Skripts // cacht mapfile function cache_mapfile($url) { $cache_file = MAPCACHE . "/". md5($url); if (!file_exists($cache_file)) { file_put_contents($cache_file, file_get_contents($url)); } return $cache_file; } // trennzeichen für positionsschlüssel $pos_delimiter = '|'; // Bergfeld cachen cache_mapfile(BERGFELD); // maplist holen if ($o['verbose']) echo "Felder aus ".MAPLIST." holen und Bilder laden...\n"; $handle = fopen(MAPLIST, "r"); if (!$handle) { echo "Kann Feldliste aus ".MAPLIST." nicht laden\n"; exit; } $field_rows = array(); while ($data = fgetcsv($handle, 1000, ";")) { array_push($field_rows, $data); } fclose($handle); // felder in `position` => `url` format überführen $fields = []; foreach ($field_rows as $field) { $x = array_key_exists(2, $field) ? $field[2] : NULL; $y = array_key_exists(3, $field) ? $field[3] : NULL; $g = array_key_exists(0, $field) ? $field[0] : NULL; $b = array_key_exists(1, $field) ? $field[1] : NULL; if ($o["mode"] == "gesamt") { // Für die Gesamtkarte werden die Felder des Kontinents als ein Gebiet // behandelt if ($x>GESAMTKARTE_LORU[0] && $y>GESAMTKARTE_LORU[1] && $x<GESAMTKARTE_LORU[2] && $y<GESAMTKARTE_LORU[3]) { $g="Oberfläche"; } else { // Dungeons (und Außenbereiche des Kontinents wie Narubia) // nur, wenn --dungeons gesetzt ist if (!$o['dungeons']) continue; } } else { // Für Einzelkarten und Atlas kann man mit $dungeons steuern, // ob auch Dungeons ausgegeben werden sollen if (!$o['dungeons'] && $x<2) continue; } $lim = &$gebiet_limits[$g]; if (!$lim) { $lim["minx"] = 99999; $lim["miny"] = 99999; $lim["maxx"] = -99999; $lim["maxy"] = -99999; } // Hier wird die Größe jedes Gebiets ermittelt; dazu berücksichtigen // wir aber nur die begehbaren Teile, sonst haben Karten wie z.B. // Düsterfrostinsel einen unnötig großen Platzbedarf. TODO, dies // konfigurierbar machen if ($b) { if ($x < $lim["minx"]) $lim["minx"] = $x; if ($y < $lim["miny"]) $lim["miny"] = $y; if ($x > $lim["maxx"]) $lim["maxx"] = $x; if ($y > $lim["maxy"]) $lim["maxy"] = $y; } // und gleich bilddatein holen $cache_file = cache_mapfile(array_key_exists(5, $field) ? $field[5] : NULL); // kein Feldtitel oder `Feldtitel Pensal (brennend)` if (!isset($g) || strpos($g, ' (brennend)') === false) { $fields["{$x}$pos_delimiter{$y}"] = [ 'x' => $x, 'y' => $y, 'g' => $g, 'begehbar' => $b, 'file' => $cache_file ]; } } // Randfelder einfügen foreach ($fields as $field) { // Randfelder for ($diff_x = -1; $diff_x <= 1; ++$diff_x) { for ($diff_y = -1; $diff_y <= 1; ++$diff_y) { $edge = ($field['x'] + $diff_x) . $pos_delimiter . ($field['y'] + $diff_y); if (!isset($fields[$edge])) { $fields[$edge] = [ 'x' => $field['x'] + $diff_x, 'y' => $field['y'] + $diff_y, 'file' => MAPCACHE . '/' . md5(BERGFELD) ]; } } } } # Groesse eines Kartenfelds feststellen list($tilewidth, $tileheight) = getimagesize($cache_file); if ($o['verbose']) echo "tile size: $tilewidth x $tileheight\n"; // ein weisses Kartenfeld herstellen if ($o['lighten']) { $aufheller = imagecreatetruecolor($tilewidth, $tileheight); $farbe = imagecolorallocate($aufheller, 255, 255, 255); imagefilledrectangle($aufheller, 0, 0, $tilewidth, $tileheight, $farbe); } if ($o["mode"] == 'atlas') { file_put_contents("odt/content.xml", file_get_contents("head.xml")); $dungeons = ""; $oberflaeche = ""; } foreach($gebiet_limits as $gebiet => $limits) { // Bereich ermitteln $max_x_found = $limits['maxx'] + 1; $max_y_found = $limits['maxy'] + 1; $min_x_found = $limits['minx'] - 1; $min_y_found = $limits['miny'] - 1; // Leeres Kartenbild erstellen $mapwidth = ($max_x_found - $min_x_found + ($o['grid'] ? 3 : 1)) * $tilewidth + ($max_x_found - $min_x_found + 2) * $o['cellspacing']; $mapheight = ($max_y_found - $min_y_found + ($o['grid'] ? 3 : 1)) * $tileheight + ($max_y_found - $min_y_found + 2) * $o['cellspacing']; if ($o['verbose']) { echo "Karte für '$gebiet': x_min: $min_x_found; x_max: $max_x_found; y_min: $min_y_found; y_max: $max_y_found; "; echo "Bildgröße: $mapwidth x $mapheight\n"; } $mapimage = imagecreatetruecolor($mapwidth, $mapheight); if (!$mapimage) { echo "Fehler bei der Erstellung des Kartenbilds für '$gebiet'\n"; continue; } // Hintergrundfarbe setzen $bgarray = sscanf($o['bgcolor'], "%02X%02X%02X"); $bgindex = imagecolorallocate($mapimage, $bgarray[0], $bgarray[1], $bgarray[2]); imagefill($mapimage, 0, 0, $bgindex); // Gitternetz einzeichnen und ggf. beschriften if ($o['grid']) { for ($x = $min_x_found; $x <= $max_x_found; $x++) { if ($x%$o['grid'] == 0) { $mpx = ($x - $min_x_found + 1) * ($tilewidth + $o['cellspacing']) + $o['cellspacing'] + $tilewidth/2; imageline($mapimage, $mpx, $o['rotatelabels'] ? 0 : 40, $mpx, $o['rotatelabels'] ? $mapheight : $mapheight - 40, 0); if (!$o['gridlabels']) continue; $t = $x; if (strlen($t) > 4) $t=substr($t,0,2)."\n".substr($t,3); // X-Achsen-Labels können entweder gerade stehen oder // entlang der Achse (rotatelabels) if ($o['rotatelabels']) { imagettftext($mapimage, 11, 270, $mpx + 3, 5, 0, $o['labelfont'], $t); $ar = imagettfbbox (11, 270, $o['labelfont'], $t); imagettftext($mapimage, 11, 270, $mpx + 3, $mapheight - $ar[3] - 5, 0, $o['labelfont'], $t); } else { $ar = imagettfbbox (11, 0, $o['labelfont'], $t); imagettftext($mapimage, 11, 0, $mpx + 3 - $ar[4]/2, 22 - $ar[5], 0, $o['labelfont'], $t); imagettftext($mapimage, 11, 0, $mpx + 3 - $ar[4]/2, $mapheight + $ar[5] - 10, 0, $o['labelfont'], $t); } } } for ($y = $min_y_found; $y <= $max_y_found; $y++) { if ($y%$o['grid'] == 0) { $mpy = ($y - $min_y_found + 1) * ($tileheight + $o['cellspacing']) + $o['cellspacing'] + $tileheight/2; imageline($mapimage, 0, $mpy, $mapwidth, $mpy, 0); if (!$o['gridlabels']) continue; $t = $y; if (strlen($t) > 4) $t=substr($t,0,2)."\n".substr($t,3); imagettftext($mapimage, 11, 0, 5, $mpy - 2, 0, $o['labelfont'], $t); $ar = imagettfbbox (11, 0, $o['labelfont'], $t); imagettftext($mapimage, 11, 0, $mapwidth - 5 - $ar[4], $mpy - 2, 0, $o['labelfont'], $t); } } } // Label für Gebiet einzeichnen; schwarze Box, darin weisse Box, // darin Text if ($o['gebietlabel']) { $ar = imagettfbbox (16, 0, $o['labelfont'], $gebiet); // FIXME die Zahlen hier sind etwas magisch durch Ausprobieren // gewählt imagefilledrectangle($mapimage, $mapwidth - $ar[4] - 55, $mapheight + $ar[7] - 26, $mapwidth-51, $mapheight-21, 0); imagefilledrectangle($mapimage, $mapwidth - $ar[4] - 54, $mapheight + $ar[7] - 25, $mapwidth-52, $mapheight-22, $bgindex); imagettftext($mapimage, 16, 0, $mapwidth - $ar[4] - 52, $mapheight + $ar[7] - 7, 0, $o['labelfont'], $gebiet); } // Feldbilder an die richtige Stelle im Gebiet einzeichnen. // Diese Schleife geht alle Felder durch, die im Rechteck liegen, das // das Gebiet umgibt. foreach ($fields as $key => $data) { // weiter, wenn das Feld ausserhalb ist if ($data['x'] < $min_x_found) continue; if ($data['x'] > $max_x_found) continue; if ($data['y'] < $min_y_found) continue; if ($data['y'] > $max_y_found) continue; // Feld kopieren $offset = ($o['grid']) ? 1 : 0; imagecopy($mapimage, imagecreatefromjpeg($data['file']), ($data['x'] - $min_x_found + $offset) * ($tilewidth + $o['cellspacing']) + $o['cellspacing'], ($data['y'] - $min_y_found + $offset) * ($tileheight + $o['cellspacing']) + $o['cellspacing'], 0, 0, $tilewidth, $tileheight); // Feld aufhellen, wenn es nicht zum Gebiet gehört if ($o['lighten'] && (!array_key_exists('begehbar', $data) || !$data['begehbar'] || $gebiet != $data['g'])) { imagecopymerge($mapimage, $aufheller, ($data['x'] - $min_x_found + $offset) * ($tilewidth + $o['cellspacing']) + $o['cellspacing'], ($data['y'] - $min_y_found + $offset) * ($tileheight + $o['cellspacing']) + $o['cellspacing'], 0, 0, $tilewidth, $tileheight, 50); } } // Ausgabe des Bildes für ein Gebiet. Im Gesamtkartenmodus gibt es nur // ein Gebiet, also auch nur eine Ausgabe; in den anderen Modi passiert // das hier öfter. if ($o["mode"] == 'atlas') { // Für die Atlas-Ausgabe werden Bilder, die nicht auf die Seite // passen, eventuell gedreht oder zweigeteilt oder beides. Mehr // ist aber nicht drin - wenn das Bild auch gedreht und zweigeteilt // nicht passt, fliegt es raus. $px_per_cm = $o['dpi'] / 2.54; // Bildgröße in cm $w = $mapwidth / $px_per_cm; $h = $mapheight / $px_per_cm; // Bild-Pixel-Spalte, an der wir durchschneiden, wenn nötig // (wird gerundet auf ganze Felder, damit wir nicht mitten in // einem Feld schneiden) $cutoff = floor($o['pagewidth'] * $px_per_cm / ($tilewidth + $o['cellspacing'])) * ($tilewidth + $o['cellspacing']) - $o['cellspacing']/2 + 2; // Leeres Array initialisieren; eventuell haben wir mehr als // ein Ausgabebild $images = array(); if ($w <= $o['pagewidth'] && $h <= $o['pageheight']) { // alles super, Bild passt auf Seite array_push($images, $mapimage); } elseif ($h <= $o['pagewidth'] && $w <= $o['pageheight']) { // Bild passt, wenn wir es drehen array_push($images, imagerotate($mapimage, 90, $bgindex)); imagedestroy($mapimage); } elseif ($w <= 2* $o['pagewidth'] && $h <= $o['pageheight']) { // Bild passt, wenn wir es durchschneiden - also schneiden // und zwei Teile in Ausgabe-Array stecken // TODO: Formel 2*pagewidth nicht ganz korrekt, da cutoff // bedeutet, dass wir die Seite nicht 100% ausnutzen array_push($images, imagecrop($mapimage, [ 'x' => 0, 'y' => 0, 'height' => $mapheight, 'width' => $cutoff ])); array_push($images, imagecrop($mapimage, [ 'x' => $cutoff, 'y' => 0, 'height' => $mapheight, 'width' => $mapwidth - $cutoff])); imagedestroy($mapimage); } elseif ($w <= $o['pageheight'] && $h <= 2 * $o['pagewidth']) { // Bild passt rotiert und geschnitten $m = imagerotate($mapimage, 90, $bgindex); imagedestroy($mapimage); $mapimage = $m; $temp = $w; $w = $h; $h = $temp; array_push($images, imagecrop($m, [ 'x' => 0, 'y' => 0, 'height' => $mapwidth, 'width' => $cutoff ])); array_push($images, imagecrop($m, [ 'x' => $cutoff, 'y' => 0, 'height' => $mapwidth, 'width' => $mapheight - $cutoff])); imagedestroy($m); } else { echo "Bild für '$gebiet' zu groß. Wähle höhere DPI oder größeres Papier\n"; } // Wir führen zwei Ausgabedokumente, eins mit den oberirdischen // Gebieten und eins mit den Dungeons: if ($min_x_found < 0) { $xml = & $dungeons; } else { $xml = & $oberflaeche; } // OpenOffice-XML an das passende Dokument anfügen $xml .= '<text:h text:style-name="Heading_20_2" text:outline-level="2">'. $gebiet."</text:h>"; foreach ($images as $image) { $count++; // TODO: Dieser Dateiname muss eigentlich eine Prüfsumme des Inhalts // sein. Ist er es nicht, meldet LibreOffice immer ein "defektes // Dokument", bietet aber eine Reparatur an. ob_start(); imagepng($image); $imagestring = ob_get_clean(); $imagename = sprintf("Pictures/10000000%08X%08XDEADBEEF%08X.png", imagesx($image), imagesy($image), $count); $zipfile->addFromString($imagename, $imagestring); $h = imagesy($image) / $px_per_cm; $w = imagesx($image) / $px_per_cm; $xml .= <<<EOF <text:p text:style-name="P1"> <draw:frame draw:style-name="fr1" draw:name="Image$count" text:anchor-type="as-char" svg:width="${w}cm" svg:height="${h}cm" draw:z-index="0"> <draw:image xlink:href="$imagename" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" loext:mime-type="image/png"/> </draw:frame> </text:p> EOF; imagedestroy($image); } } elseif ($o['mode'] == 'einzel' || ($o['mode'] == 'gesamt' && $o['dungeons'])) { // Bei Einzelbildausgabe einfach PNG schreiben imagepng($mapimage, $o['output']."/".$gebiet.".png"); imagedestroy($mapimage); } else { // Bei Gesamtbild traditionell JPG // TODO was wenn ein .png als Ausgabename gewählt wurde? imagejpeg($mapimage, $o['output']); imagedestroy($mapimage); } } if ($o["mode"] == 'atlas') { $old_contents = $zipfile->getFromName('content.xml'); if (!$old_contents) { echo ATLAS_TEMPLATE." enthält keine Datei content.xml - kein .odt-Dokument?\n"; } if (!preg_match('/(.*)<text:p[^<]*<text:span[^<]*ADD_CONTENT_HERE[^<]*<\/text:span>[^<]*<\/text:p>(.*)/', $old_contents, $matches)) { if (!preg_match('/(.*)<text:p[^<]*ADD CONTENT HERE[^<]*<\/text:p>(.*)/', $old_contents, $matches)) { echo "Die contents.xml im Atlas-Template entspricht nicht der erwarteten Form.\n"; echo $old_contents; exit; } } $contents = $matches[1] . $oberflaeche . $dungeons . $matches[2]; $zipfile->addFromString('content.xml', $contents); $zipfile->close(); echo "Atlas erstellt. Die fertige Datei muss nun im OpenOffice/LibreOffice\n"; echo "geöffnet und \"repariert\" werden.\n"; }