All checks were successful
Deploy bericht / deploy (push) Successful in 13s
Anhaenge-Spalte: Kachelraster mit Server-Thumbnails statt Dateinamen-Liste (Bilder per GD, PDFs per PDF.js), Grossansicht per Klick, Auswahlreihenfolge sichtbar, Sortierung/Filter (Aufnahmezeit per eigenem EXIF-Parser, da im Prod-Container ohne exif-Modul), ziehbare Spaltenbreite, Kachelgroesse, Drag & Drop auf Seitenliste/Arbeitsflaeche/Raster-Plaetze. Arbeitstisch zeigt jetzt Kopf-, Inhalts- und Fussbereich passend zur echten Seitengeometrie (bericht_page_geometry()) - Editor und PDF-Erzeugung rechnen mit denselben Werten, das PDF setzt die Arbeitsflaeche 1:1 statt sie ein zweites Mal einzupassen. Neu: Bild einer Seite austauschen (auch einzelne Raster-Plaetze), Seiten mehrfach markieren und sammeln loeschen/duplizieren, Autosave mit Verlassen-Warnung, Tastenkuerzel, kein location.reload() mehr nach Hinzufuegen/Loeschen/Hochladen (ajax/fragments.php). Nebenbei behoben: dol_dir_list() ohne mode=1 (Dateigroesse leer), PDF.js-Worker-Pfad ohne /custom-Praefix, PDFs liefen als Download statt inline, Groessen-Select blieb leer, Mehrfach-Upload nahm nur die erste Datei, Race-Condition bei schnellem Seitenwechsel. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
136 lines
5.9 KiB
PHP
136 lines
5.9 KiB
PHP
<?php
|
|
/* Liefert das Bild/PDF einer Bericht-Seite zur Anzeige im Editor.
|
|
* GET: pageid
|
|
*
|
|
* Bei source_type=image: Original-Bild
|
|
* Bei source_type=pdf: PDF wird direkt ausgeliefert (PDF.js rendert clientseitig)
|
|
* Bei source_type=upload: ebenso, je nach Endung
|
|
*/
|
|
$res = 0;
|
|
if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
|
|
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; $tmp2 = realpath(__FILE__); $i = strlen($tmp) - 1; $j = strlen($tmp2) - 1;
|
|
while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) { $i--; $j--; }
|
|
if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php";
|
|
if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php";
|
|
if (!$res && file_exists("../../main.inc.php")) $res = @include "../../main.inc.php";
|
|
if (!$res && file_exists("../../../main.inc.php")) $res = @include "../../../main.inc.php";
|
|
if (!$res) die("Include of main fails");
|
|
|
|
require_once __DIR__.'/../lib/bericht.lib.php';
|
|
|
|
if (!$user->hasRight('bericht', 'read')) accessforbidden();
|
|
|
|
$pageid = GETPOSTINT('pageid');
|
|
if (!$pageid) { http_response_code(400); exit; }
|
|
|
|
$res = $db->query("SELECT source_type, source_path, COALESCE(layout,'single') AS layout FROM ".$db->prefix()."bericht_page WHERE rowid = ".((int) $pageid));
|
|
if (!$res) { http_response_code(500); exit; }
|
|
$row = $db->fetch_object($res);
|
|
if (!$row) { http_response_code(404); exit; }
|
|
|
|
// Bei Multi-Image-Seiten: serverseitig ein Composite-Bild rendern (PNG)
|
|
if ($row->layout !== 'single') {
|
|
// Format/Orientierung des Berichts holen, damit die Vorschau dieselbe
|
|
// Proportion hat wie der Inhaltsbereich im Editor und im PDF
|
|
$bfmt = 'A4'; $bori = 'P';
|
|
$bq = $db->query("SELECT b.page_format, b.page_orientation FROM ".$db->prefix()."bericht b"
|
|
." INNER JOIN ".$db->prefix()."bericht_page p ON p.fk_bericht = b.rowid"
|
|
." WHERE p.rowid = ".((int) $pageid));
|
|
if ($bq && ($brow = $db->fetch_object($bq))) {
|
|
if (in_array($brow->page_format, array('A4','A3','A5','Letter'), true)) $bfmt = $brow->page_format;
|
|
if (in_array($brow->page_orientation, array('P','L'), true)) $bori = $brow->page_orientation;
|
|
}
|
|
$imgs_q = $db->query("SELECT slot, source_path FROM ".$db->prefix()."bericht_page_image WHERE fk_page = ".((int) $pageid)." ORDER BY slot");
|
|
if (!$imgs_q) { http_response_code(500); exit; }
|
|
$imgs = array();
|
|
while ($r = $db->fetch_object($imgs_q)) {
|
|
$imgs[(int) $r->slot] = $r->source_path;
|
|
}
|
|
if (empty($imgs)) { http_response_code(404); exit; }
|
|
|
|
$composite = bericht_render_grid_png($row->layout, $imgs, bericht_page_geometry($bfmt, $bori, false));
|
|
if (!$composite) { http_response_code(500); exit; }
|
|
header('Content-Type: image/png');
|
|
header('Cache-Control: no-store');
|
|
echo $composite;
|
|
exit;
|
|
}
|
|
|
|
$full = bericht_resolve_data_path($row->source_path);
|
|
if (!$full || !file_exists($full)) { http_response_code(404); exit; }
|
|
|
|
$mime = dol_mimetype($full);
|
|
header('Content-Type: '.$mime);
|
|
header('Content-Length: '.filesize($full));
|
|
header('Cache-Control: private, max-age=300');
|
|
readfile($full);
|
|
|
|
/**
|
|
* Erstellt ein Composite-PNG für eine Multi-Image-Seite (Editor-Vorschau).
|
|
*/
|
|
function bericht_render_grid_png($layout, $imgs, array $g)
|
|
{
|
|
// Grundfläche = Inhaltsbereich der Seite bei ~150 DPI, damit die Vorschau
|
|
// exakt der Fläche entspricht, die das PDF später bedruckt.
|
|
$px_per_mm = 150 / 25.4;
|
|
$W = (int) round($g['contentW'] * $px_per_mm);
|
|
$H = (int) round($g['contentH'] * $px_per_mm);
|
|
$gap = (int) round(4 * $px_per_mm);
|
|
|
|
$canvas = imagecreatetruecolor($W, $H);
|
|
imagefill($canvas, 0, 0, imagecolorallocate($canvas, 255, 255, 255));
|
|
|
|
// Slot-Aufteilung wie BerichtPage::slotRectsInBox, hier in Pixel
|
|
$rects = array();
|
|
if ($layout === 'grid_2') {
|
|
$h = ($H - $gap) / 2;
|
|
for ($i = 0; $i < 2; $i++) $rects[] = array(0, $i * ($h + $gap), $W, $h);
|
|
} elseif ($layout === 'grid_2v' || $layout === 'before_after') {
|
|
$w = ($W - $gap) / 2;
|
|
for ($i = 0; $i < 2; $i++) $rects[] = array($i * ($w + $gap), 0, $w, $H);
|
|
} elseif ($layout === 'grid_4') {
|
|
$w = ($W - $gap) / 2;
|
|
$h = ($H - $gap) / 2;
|
|
for ($r = 0; $r < 2; $r++) for ($c = 0; $c < 2; $c++) {
|
|
$rects[] = array($c * ($w + $gap), $r * ($h + $gap), $w, $h);
|
|
}
|
|
} elseif ($layout === 'grid_6') {
|
|
$w = ($W - 2 * $gap) / 3;
|
|
$h = ($H - $gap) / 2;
|
|
for ($r = 0; $r < 2; $r++) for ($c = 0; $c < 3; $c++) {
|
|
$rects[] = array($c * ($w + $gap), $r * ($h + $gap), $w, $h);
|
|
}
|
|
}
|
|
|
|
foreach ($rects as $i => $r) {
|
|
list($rx, $ry, $rw, $rh) = $r;
|
|
// Slot-Hintergrund + Border
|
|
$border = imagecolorallocate($canvas, 200, 200, 200);
|
|
imagerectangle($canvas, $rx, $ry, $rx + $rw, $ry + $rh, $border);
|
|
|
|
if (!isset($imgs[$i])) continue;
|
|
$full = bericht_resolve_data_path($imgs[$i]);
|
|
if (!$full || !file_exists($full)) continue;
|
|
|
|
$info = @getimagesize($full);
|
|
if (!$info) continue;
|
|
$src = null;
|
|
if ($info[2] === IMAGETYPE_JPEG) $src = @imagecreatefromjpeg($full);
|
|
elseif ($info[2] === IMAGETYPE_PNG) $src = @imagecreatefrompng($full);
|
|
if (!$src) continue;
|
|
|
|
$sw = $info[0]; $sh = $info[1];
|
|
$ratio = min($rw / $sw, $rh / $sh);
|
|
$dw = (int) ($sw * $ratio);
|
|
$dh = (int) ($sh * $ratio);
|
|
$dx = (int) ($rx + ($rw - $dw) / 2);
|
|
$dy = (int) ($ry + ($rh - $dh) / 2);
|
|
imagecopyresampled($canvas, $src, $dx, $dy, 0, 0, $dw, $dh, $sw, $sh);
|
|
imagedestroy($src);
|
|
}
|
|
|
|
ob_start();
|
|
imagepng($canvas, null, 6);
|
|
imagedestroy($canvas);
|
|
return ob_get_clean();
|
|
}
|