Commit 1bf257c9 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Created

parent eec03363
......@@ -62,6 +62,27 @@ COPY . /var/www/html/
WORKDIR /var/www/html
RUN composer install --no-dev --optimize-autoloader --no-interaction --no-scripts
# ── Create optimized JPG copies of screenshots for PDF export ──
RUN if [ -d /var/www/html/public/assets/tutorials/screenshots ]; then \
php -r ' \
$dir = "/var/www/html/public/assets/tutorials/screenshots"; \
foreach (glob("$dir/*.png") as $f) { \
$img = @imagecreatefrompng($f); \
if (!$img) continue; \
$w = imagesx($img); $h = imagesy($img); \
if ($w > 900) { \
$nw = 900; $nh = (int)($h * 900 / $w); \
$dst = imagecreatetruecolor($nw, $nh); \
imagecopyresampled($dst, $img, 0, 0, 0, 0, $nw, $nh, $w, $h); \
imagedestroy($img); $img = $dst; \
} \
$jpg = substr($f, 0, -4) . ".jpg"; \
imagejpeg($img, $jpg, 55); \
imagedestroy($img); \
} \
'; \
fi
# ── Create storage directories ──
RUN mkdir -p \
/var/www/html/storage/logs \
......
......@@ -572,8 +572,6 @@ class TutorialController extends Controller
include __DIR__ . '/../Views/export_pdf.php';
$html = ob_get_clean();
$html = $this->inlineImages($html);
$wkhtmltopdf = null;
foreach (['/usr/local/bin/wkhtmltopdf', '/usr/bin/wkhtmltopdf'] as $path) {
if (file_exists($path) && is_executable($path)) {
......@@ -590,16 +588,15 @@ class TutorialController extends Controller
$tmpOutput = tempnam(sys_get_temp_dir(), 'pdf_out_') . '.pdf';
file_put_contents($tmpInput, $html);
$cmd = 'xvfb-run --auto-servernum --server-args="-screen 0 1280x1024x24" '
. escapeshellarg($wkhtmltopdf)
$cmd = escapeshellarg($wkhtmltopdf)
. ' --encoding utf-8 --page-size A4'
. ' --margin-top 15 --margin-bottom 15 --margin-left 12 --margin-right 12'
. ' --enable-local-file-access'
. ' --no-stop-slow-scripts'
. ' --javascript-delay 100'
. ' --image-quality 70'
. ' --image-dpi 150'
. ' --print-media-type'
. ' --disable-javascript'
. ' --image-quality 60'
. ' --image-dpi 100'
. ' --lowquality'
. ' ' . escapeshellarg($tmpInput)
. ' ' . escapeshellarg($tmpOutput)
. ' 2>&1';
......@@ -696,9 +693,13 @@ class TutorialController extends Controller
$categories = TutorialRegistry::getCategories($sectionKey);
$screenshot = null;
if ($screenshotsPath) {
$base = $sectionKey;
$baseAlt = str_replace('-', '_', $sectionKey);
$possibleFiles = [
$screenshotsPath . '/' . $sectionKey . '.png',
$screenshotsPath . '/' . str_replace('-', '_', $sectionKey) . '.png',
$screenshotsPath . '/' . $base . '.jpg',
$screenshotsPath . '/' . $base . '.png',
$screenshotsPath . '/' . $baseAlt . '.jpg',
$screenshotsPath . '/' . $baseAlt . '.png',
];
foreach ($possibleFiles as $f) {
if (file_exists($f)) {
......@@ -719,31 +720,6 @@ class TutorialController extends Controller
return $book;
}
private function inlineImages(string $html): string
{
return (string) preg_replace_callback(
'#src="file://([^"]+\.(png|jpg|jpeg|gif|webp))"#i',
function (array $m): string {
$path = $m[1];
if (!file_exists($path)) {
return $m[0];
}
$raw = file_get_contents($path);
if ($raw === false) {
return $m[0];
}
$mime = match (strtolower($m[2])) {
'jpg', 'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'webp' => 'image/webp',
default => 'image/png',
};
return 'src="data:' . $mime . ';base64,' . base64_encode($raw) . '"';
},
$html
);
}
private function extractTutorialContent(string $raw): string
{
$start = strpos($raw, '<div class="tut-page">');
......@@ -765,9 +741,18 @@ class TutorialController extends Controller
$content = str_replace('loading="lazy"', '', $content);
$publicPath = realpath(__DIR__ . '/../../../../public');
$content = preg_replace(
$content = (string) preg_replace_callback(
'#src="(/assets/[^"]+)"#',
'src="file://' . $publicPath . '$1"',
function (array $m) use ($publicPath): string {
$rel = $m[1];
if (str_ends_with($rel, '.png')) {
$jpgPath = $publicPath . substr($rel, 0, -4) . '.jpg';
if (file_exists($jpgPath)) {
return 'src="file://' . $jpgPath . '"';
}
}
return 'src="file://' . $publicPath . $rel . '"';
},
$content
);
......
......@@ -191,7 +191,7 @@ function startExport() {
}
}, 2000);
// Safety timeout: 5 minutes max
// Safety timeout: 10 minutes max
setTimeout(function() {
if (document.getElementById('duringExport').style.display !== 'none') {
clearInterval(cookieTimer);
......@@ -201,7 +201,7 @@ function startExport() {
var f = document.getElementById('exportFrame');
if (f) f.remove();
}
}, 300000);
}, 600000);
}
function finishExport(success) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment