generated from Nodarx/template
1bf71898db
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
106 lines
4.9 KiB
PHP
106 lines
4.9 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
namespace KTXM\ServiceWopi\Preview;
|
|
|
|
use Imagick;
|
|
use KTXC\Context\TenantContextInterface;
|
|
use KTXF\Preview\MimeType;
|
|
use KTXF\Preview\PreviewGenerationException;
|
|
use KTXF\Preview\PreviewRequest;
|
|
use KTXF\Preview\PreviewResult;
|
|
use KTXF\Preview\PreviewSource;
|
|
use KTXF\Preview\Provider\PreviewProviderInterface;
|
|
use KTXF\Resource\BinaryResource;
|
|
use KTXF\Resource\Provider\ProviderInterface;
|
|
use KTXM\ProviderPreviewImageMagick\Provider as ImageProvider;
|
|
use KTXM\ServiceWopi\Host\WopiService;
|
|
use Throwable;
|
|
|
|
final class Provider implements PreviewProviderInterface
|
|
{
|
|
public function __construct(
|
|
private readonly TenantContextInterface $tenant,
|
|
private readonly WopiService $wopi,
|
|
private readonly ConversionClient $client,
|
|
private readonly ImageProvider $images,
|
|
) {}
|
|
|
|
public function type(): string { return ProviderInterface::TYPE_PREVIEW; }
|
|
public function identifier(): string { return 'wopi'; }
|
|
public function label(): string { return 'WOPI Office'; }
|
|
public function description(): string { return 'Generates first-page previews using the configured office server'; }
|
|
public function icon(): string { return 'file-document'; }
|
|
public function priority(): int { return 100; }
|
|
|
|
public function supports(string $sourceMimeType, PreviewRequest $request): bool
|
|
{
|
|
try {
|
|
if (!class_exists(Imagick::class)
|
|
|| !in_array(MimeType::normalize($sourceMimeType), WopiService::FORMATS, true)
|
|
|| !$this->images->supports('image/png', $request)) {
|
|
return false;
|
|
}
|
|
$this->wopi->configuration($this->tenant->requireIdentifier());
|
|
return true;
|
|
} catch (Throwable) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function generate(PreviewSource $source, PreviewRequest $request): PreviewResult
|
|
{
|
|
if (!$this->supports($source->mimeType, $request)) {
|
|
throw new PreviewGenerationException('WOPI does not support the requested preview conversion');
|
|
}
|
|
$config = $this->wopi->configuration($this->tenant->requireIdentifier());
|
|
$limit = min($request->maxSourceSize, $config->maxBytes);
|
|
if ($source->size !== null && $source->size > $limit) {
|
|
throw new PreviewGenerationException('Preview source exceeds the configured size limit');
|
|
}
|
|
$content = '';
|
|
foreach ($source->open()->stream() as $chunk) {
|
|
if (!is_string($chunk) || strlen($content) + strlen($chunk) > $limit) {
|
|
throw new PreviewGenerationException('Invalid or oversized preview source');
|
|
}
|
|
$content .= $chunk;
|
|
}
|
|
if ($content === '') { throw new PreviewGenerationException('Preview source is empty'); }
|
|
|
|
$pdf = $this->client->pdf($config->office, $content, array_search($source->mimeType, WopiService::FORMATS, true), $source->mimeType);
|
|
$file = tmpfile();
|
|
if ($file === false) { throw new PreviewGenerationException('Cannot create preview temporary file'); }
|
|
$image = new Imagick();
|
|
try {
|
|
if (fwrite($file, $pdf) !== strlen($pdf)) {
|
|
throw new PreviewGenerationException('Cannot write preview temporary file');
|
|
}
|
|
$image->setResolution(96, 96);
|
|
$image->readImage('pdf:' . stream_get_meta_data($file)['uri'] . '[0]');
|
|
// Treat transparent PDF areas as paper in every output format.
|
|
$image->setImageBackgroundColor('white');
|
|
$image->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
|
|
// Scale the inset outline so it remains about one pixel after resizing.
|
|
$stroke = max($image->getImageWidth() / $request->maxWidth, $image->getImageHeight() / $request->maxHeight, 1);
|
|
$outline = new \ImagickDraw();
|
|
$outline->setFillColor('transparent');
|
|
$outline->setStrokeColor('#b8b8b8');
|
|
$outline->setStrokeWidth($stroke);
|
|
$outline->rectangle($stroke / 2, $stroke / 2,
|
|
$image->getImageWidth() - $stroke / 2, $image->getImageHeight() - $stroke / 2);
|
|
$image->drawImage($outline);
|
|
$outline->clear();
|
|
$image->setImageFormat('png');
|
|
$png = $image->getImageBlob();
|
|
return $this->images->generate(new PreviewSource(
|
|
$source->sourceType, $source->identity, $source->signature, 'image/png', strlen($png),
|
|
static fn () => new BinaryResource('page.png', 'image/png', (static function () use ($png): \Generator { yield $png; })()),
|
|
), new PreviewRequest($request->maxWidth, $request->maxHeight, $request->preferredMimeType, $request->quality, ConversionClient::MAX_RESPONSE_BYTES));
|
|
} catch (Throwable $error) {
|
|
throw new PreviewGenerationException('Could not render the office preview', previous: $error);
|
|
} finally {
|
|
$image->clear();
|
|
fclose($file);
|
|
}
|
|
}
|
|
}
|