Add tex compilation

This commit is contained in:
Philipp Lang 2022-10-28 11:55:09 +02:00
parent 8892b11dc7
commit fb9d7a660c
6 changed files with 137 additions and 149 deletions

61
src/BaseCompiler.php Normal file
View File

@ -0,0 +1,61 @@
<?php
namespace Zoomyboy\Tex;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\File;
use Illuminate\Support\Str;
abstract class BaseCompiler implements Responsable
{
protected File $file;
public function compile(Document $document): self
{
$this->prepareForCompilation($document);
$contents = $document->renderBody();
file_put_contents("{$this->file->getPath()}/{$document->filename()}", $contents);
if ($document->template()) {
$templatePath = $document->template()->fullPath();
exec('rsync -av '.escapeshellarg($templatePath).'/ '.escapeshellarg($this->file->getPath()));
}
exec($this->command($document), $output, $returnVar);
$this->refreshFile();
throw_unless($this->file->isFile(), CompilerException::class, 'Compilation failed.');
return $this;
}
public function toResponse($request)
{
return response()->file($this->file->getRealPath(), [
'Content-Type' => 'application/pdf',
'Content-Disposition' => "inline; filename=\"{$this->file->getFilename()}\"",
]);
}
protected function prepareForCompilation(Document $document): void
{
$workDir = '/tmp/'.Str::random(32);
mkdir($workDir, 0777, true);
$this->file = new File($workDir.'/'.$document->compiledFilename(), false);
}
private function refreshFile(): void
{
$this->file = new File($this->file->getPathname(), false);
}
private function command(Document $document): string
{
return collect([
'cd '.escapeshellarg($this->file->getPath()),
$document->getEngine()->binary().' --halt-on-error '.$document->filename(),
$document->getEngine()->binary().' --halt-on-error '.$document->filename(),
])->implode(' && ');
}
}

View File

@ -2,52 +2,6 @@
namespace Zoomyboy\Tex; namespace Zoomyboy\Tex;
use Illuminate\Contracts\Support\Responsable; class Compiler extends BaseCompiler
use Illuminate\Http\File;
use Illuminate\Support\Str;
class Compiler implements Responsable
{ {
public File $file;
public function compile(Document $document): self
{
$filename = $document->filename();
$dir = Str::random(32);
$contents = $document->renderBody();
mkdir('/tmp/'.$dir);
file_put_contents('/tmp/'.$dir.'/'.$document->filename().'.tex', $contents);
if ($document->template()) {
$templatePath = $document->template()->fullPath();
exec('rsync -av '.escapeshellarg($templatePath).'/ /tmp/'.escapeshellarg($dir));
}
$command = collect([
'cd /tmp/'.escapeshellarg($dir),
$document->getEngine()->binary().' --halt-on-error '.$document->filename().'.tex',
$document->getEngine()->binary().' --halt-on-error '.$document->filename().'.tex',
])->implode(' && ');
exec($command, $output, $returnVar);
dd($output);
if (file_exists('/tmp/'.$dir.'/'.$document->filename().'.pdf')) {
$this->file = new File('/tmp/'.$dir.'/'.$document->filename().'.pdf');
} else {
throw new Exception('Compilation failed');
}
return $this;
}
public function toResponse($request)
{
return response()->file($this->file->getRealPath(), [
'Content-Type' => 'application/pdf',
'Content-Disposition' => "inline; filename=\"{$this->file->getFilename()}\"",
]);
}
} }

View File

@ -2,60 +2,22 @@
namespace Zoomyboy\Tex; namespace Zoomyboy\Tex;
use Illuminate\Http\File;
use Illuminate\Http\UploadedFile; use Illuminate\Http\UploadedFile;
use Illuminate\Support\Collection;
use PHPUnit\Framework\Assert;
class CompilerFake extends Compiler class CompilerFake extends Compiler
{ {
/** use FakesCompilation;
* @var array<int, Document>
*/
private array $compiledDocuments = [];
/**
* @param class-string<Document> $documentClass
* @param callable(Document): bool $check
*/
public function assertCompiled(string $documentClass, callable $check): void
{
$compilations = $this->getCompilations($documentClass);
Assert::assertFalse(
$compilations->isEmpty(),
'The TeX Document "'.$documentClass.'" has not been compiled at all.'
);
Assert::assertFalse(
$compilations->isEmpty(),
'The TeX Document "'.$documentClass.'" has not been compiled.'
);
$validDocuments = $compilations->filter(fn ($compilation) => $check($compilation));
Assert::assertNotEmpty($validDocuments, 'Failed that TeX Document "'.$documentClass.'" has been compiled with given check.');
}
/**
* @param class-string<Document> $documentClass
*
* @return Collection<int, Document>
*/
protected function getCompilations(string $documentClass): Collection
{
return collect($this->compiledDocuments)->filter(fn ($rendered) => get_class($rendered) === $documentClass);
}
public function compile(Document $document): self public function compile(Document $document): self
{ {
$path = '/tmp/'.$document->filename().'.pdf'; $this->prepareForCompilation($document);
$file = UploadedFile::fake()->create($document->filename().'.pdf', 100, 'application/pdf');
file_put_contents($path, $file->get()); file_put_contents(
$this->file->getPathname(),
UploadedFile::fake()->create($this->file->getFilename(), 100, 'application/pdf')->get()
);
$this->file = new File($path, true); $this->registerCompilation($document);
$this->compiledDocuments[] = $document;
return $this; return $this;
} }

View File

@ -2,58 +2,17 @@
namespace Zoomyboy\Tex; namespace Zoomyboy\Tex;
use Illuminate\Support\Collection; class CompilerSpy extends BaseCompiler
use PHPUnit\Framework\Assert;
class CompilerSpy
{ {
use FakesCompilation;
public static Compiler $actualCompiler; public static Compiler $actualCompiler;
/**
* @var array<int, Document>
*/
private array $compiledDocuments = [];
/** public function compile(Document $document): BaseCompiler
* @param class-string<Document> $documentClass
* @param callable(Document): bool $check
*/
public function assertCompiled(string $documentClass, callable $check): void
{
$compilations = $this->getCompilations($documentClass);
Assert::assertFalse(
$compilations->isEmpty(),
'The TeX Document "'.$documentClass.'" has not been compiled at all.'
);
Assert::assertFalse(
$compilations->isEmpty(),
'The TeX Document "'.$documentClass.'" has not been compiled.'
);
foreach ($compilations as $compilation) {
}
Assert::assertFalse(
$this->getCompilations($documentClass)->isEmpty(),
'The TeX Document "'.$documentClass.'" has not been compiled.'
);
}
/**
* @param class-string<Document> $documentClass
*
* @return Collection<int, Document>
*/
protected function getCompilations(string $documentClass): Collection
{
return collect($this->compiledDocuments)->filter(fn ($rendered) => get_class($rendered) === $documentClass);
}
public function compile(Document $document): Compiler
{ {
$compiler = static::$actualCompiler->compile($document); $compiler = static::$actualCompiler->compile($document);
$this->compiledDocuments[] = $document; $this->registerCompilation($document);
return $compiler; return $compiler;
} }

View File

@ -2,11 +2,9 @@
namespace Zoomyboy\Tex; namespace Zoomyboy\Tex;
use PHPUnit\Framework\Assert;
abstract class Document abstract class Document
{ {
abstract public function filename(): string; abstract public function basename(): string;
abstract public function view(): string; abstract public function view(): string;
@ -14,18 +12,25 @@ abstract class Document
abstract public function getEngine(): Engine; abstract public function getEngine(): Engine;
public function assertHasContent(string $content): void
{
Assert::assertStringContainsString(
$content,
$this->renderBody(),
);
}
public function renderBody(): string public function renderBody(): string
{ {
return view() return view()
->make($this->view(), get_object_vars($this)) ->make($this->view(), get_object_vars($this))
->render(); ->render();
} }
public function hasContent(string $content): bool
{
return str_contains($this->renderBody(), $content);
}
public function filename(): string
{
return $this->basename().'.tex';
}
public function compiledFilename(): string
{
return $this->basename().'.pdf';
}
} }

47
src/FakesCompilation.php Normal file
View File

@ -0,0 +1,47 @@
<?php
namespace Zoomyboy\Tex;
use Illuminate\Support\Collection;
use PHPUnit\Framework\Assert;
trait FakesCompilation
{
/**
* @var array<int, Document>
*/
private array $compiledDocuments = [];
/**
* @param class-string<Document> $documentClass
* @param callable(Document): bool $check
*/
public function assertCompiled(string $documentClass, callable $check): void
{
$compilations = $this->getCompilations($documentClass);
Assert::assertFalse(
$compilations->isEmpty(),
'The TeX Document "'.$documentClass.'" has not been compiled.'
);
$validDocuments = $compilations->filter(fn ($compilation) => $check($compilation));
Assert::assertNotEmpty($validDocuments, 'Failed that TeX Document "'.$documentClass.'" has been compiled with given check.');
}
/**
* @param class-string<Document> $documentClass
*
* @return Collection<int, Document>
*/
protected function getCompilations(string $documentClass): Collection
{
return collect($this->compiledDocuments)->filter(fn ($rendered) => get_class($rendered) === $documentClass);
}
protected function registerCompilation(Document $document): void
{
$this->compiledDocuments[] = $document;
}
}