Skip to content

Commit

Permalink
Try wkhtmltopdf as alternate image generation tool.
Browse files Browse the repository at this point in the history
  • Loading branch information
sei-bstein committed Aug 31, 2023
1 parent 349ee79 commit d59c424
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 35 deletions.
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS prod
ARG commit
ENV COMMIT=$commit

# install chromium for PNG generation on the server
RUN apt-get update && apt-get install -y chromium && apt-get clean
# install tools for PNG generation on the server
RUN apt-get update && apt-get install -y wget && apt-get clean
RUN wget -O ~/wkhtmltopdf.deb https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.bullseye_arm64.deb
RUN apt-get install -y ~/wkhtmltopdf.deb
RUN rm ~/wkhtmltopdf.deb

COPY --from=dev /app/dist /app
COPY --from=dev /app/LICENSE.md /app/LICENSE.md
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ public async Task<FileResult> GetPracticeCertificatePng([FromRoute] string userI
return File(await _htmlToImage.ToPng($"{_actingUser.Get().Id}_{awardedForEntityId}", html, 3300, 2550), MimeTypes.ImagePng);
}

[HttpGet]
[Route("{userId}/certificates/practice/{awardedForEntityId}/pdf")]
[AllowAnonymous] // anyone can _try_, but we only serve them the cert if it's published (or if they're the owner)
public async Task<FileResult> GetPracticeCertificatePdf([FromRoute] string userId, [FromRoute] string awardedForEntityId, CancellationToken cancellationToken)
{
var html = await _mediator.Send(new GetPracticeModeCertificateHtmlQuery(awardedForEntityId, userId, _actingUser.Get()), cancellationToken);
return File(await _htmlToImage.ToPdf($"{_actingUser.Get().Id}_{awardedForEntityId}", html, 3300, 2550), MimeTypes.ApplicationPdf);
}

[HttpGet]
[Route("{userId}/certificates/competitive/{awardedForEntityId}")]
[AllowAnonymous] // anyone can _try_, but we only serve them the cert if it's published (or if they're the owner)
Expand Down
83 changes: 50 additions & 33 deletions src/Gameboard.Api/Services/HtmlToImageService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Gameboard.Api.Common.Services;
Expand Down Expand Up @@ -31,34 +30,41 @@ public HtmlToImageService(CoreOptions coreOptions)

public async Task<byte[]> ToPdf(string fileName, string htmlString, int? width = null, int? height = null)
{
var tempImageResult = await ToTempImage(fileName, htmlString, width, height);
var tempHtmlPath = Path.Combine(_coreOptions.TempDirectory, $"{fileName}.html");
var pdfPath = Path.Combine(_coreOptions.TempDirectory, $"{fileName}.pdf");
await File.WriteAllTextAsync(tempHtmlPath, htmlString);

// convert the temp image to PDF with chromium headless
var args = new string[]
{
"--headless",
"--no-sandbox",
"--disable-gpu",
"--landscape",
width != null && height != null ? $"--window-size={width.Value}x{height.Value}" : null,
$"--print-to-pdf={pdfPath}",
"--disable-pdf-tagging",
"--no-pdf-header-footer",
"--disable-dev-shm-usage",
tempImageResult.TempImagePath
}
.Where(arg => !arg.IsEmpty())
.ToArray();
"--title",
""" "Gameboard Certificate" '""",
// "--dpi",
// "300",
"--margin-top",
"0mm",
"--margin-right",
"0mm",
"--margin-bottom",
"0mm",
"--margin-left",
"0mm",
"-O",
"Landscape",
"--page-size",
"Letter",
"--no-outline",
tempHtmlPath,
pdfPath
};

// run chromium and verify
var result = await StartProcessAsync.StartAsync("chromium", args);
var result = await StartProcessAsync.StartAsync("wkhtmltopdf", args);
if (result != 0)
throw new Exception("PDF generation failed.");

var pdfBytes = await File.ReadAllBytesAsync(pdfPath);

tempImageResult.Delete();
File.Delete(tempHtmlPath);
File.Delete(pdfPath);
return pdfBytes;
}
Expand Down Expand Up @@ -91,24 +97,35 @@ private async Task<ToTempImageResult> ToTempImage(string fileName, string htmlSt
var tempImagePath = Path.Combine(_coreOptions.TempDirectory, $"{fileName}.png");
await File.WriteAllTextAsync(tempHtmlPath, htmlString);

// save it with chromium headless
// // save it with chromium headless
// var args = new string[]
// {
// "--headless",
// "--no-sandbox",
// "--disable-gpu",
// "--landscape",
// // ask chromium not to use dev shared memory - it defaults to only 64mb on docker
// "--disable-dev-shm-usage",
// width != null && height != null ? $"--window-size={width.Value}x{height.Value}" : null,
// $"--screenshot={tempImagePath}",
// tempHtmlPath
// }
// .Where(arg => !arg.IsEmpty())
// .ToArray();

// // run chromium and verify
// var result = await StartProcessAsync.StartAsync("chromium", args);

// save it with wkhtmltoimage
var args = new string[]
{
"--headless",
"--no-sandbox",
"--disable-gpu",
"--landscape",
// ask chromium not to use dev shared memory - it defaults to only 64mb on docker
"--disable-dev-shm-usage",
width != null && height != null ? $"--window-size={width.Value}x{height.Value}" : null,
$"--screenshot={tempImagePath}",
tempHtmlPath
}
.Where(arg => !arg.IsEmpty())
.ToArray();
"-f",
"png",
tempHtmlPath,
tempImagePath
};

// run chromium and verify
var result = await StartProcessAsync.StartAsync("chromium", args);
var result = await StartProcessAsync.StartAsync("wkhtmltoimage", args);
if (result != 0)
throw new Exception("Image generation failed.");

Expand Down
Binary file added src/Gameboard.Api/wwwroot/temp/this.pdf
Binary file not shown.

0 comments on commit d59c424

Please sign in to comment.