MicrosoftEdge / WebView2Samples

Microsoft Edge WebView2 samples
830 stars 460 forks source link

Is it possible to run in a Windows Container/App Service to convert HTML to PDFs? #238

Open aligneddev opened 3 months ago

aligneddev commented 3 months ago

Rick Strahl has teased the idea of using WebView2 to create PDFs

I'd like to create an Azure App Service or hosted container to accept html and spit out PDFs. Currently using itext7, but the HTML doesn't get rendered in the PDF the same and it's causing me a lot of grief. Ideally, I could create HTML in Edge/Chrome and then send it to a PDF service and have them look exactly the same, using the same rendering engine (I'm looking at you WebView2) and I could host it in Linux, but that is an open issue on Azure and support 100s of requests a minute.

I've started but I have questions

  1. Is this possible?
  2. Are there licensing issues with this approach?
  3. How would I package and deploy this? Would a docker file/image work? Could you provide a pre-made image?
  4. what load could it handle, how big of a server would I need?
aligneddev commented 3 months ago

Here's what I have so far using

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.FileProviders;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Wpf;
using System.IO;
using System.Text;
using System.Web;
using System.Windows;

namespace HtmlToPdf.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class PdfController : ControllerBase
    {
        [HttpPost("html")]
        public async Task<IActionResult> PostHtml(IList<IFormFile> files, [FromQuery] bool usePrintMediaType = false,
        bool useLandscapeOrientation = false)
        {
            var pdfFileName = $"{DateTimeOffset.UtcNow.Ticks}.pdf";
            if (files.Count == 1)
            {
                var pdf = await ConvertHtmlToPdfAsync(files.Single(), usePrintMediaType, useLandscapeOrientation);
                return File(pdf.OpenRead(), "application/pdf", pdfFileName);
            }
            else
            {
                return BadRequest();
            }
        }

        private async Task<FileInfo> ConvertHtmlToPdfAsync(IFormFile postedHtml, bool usePrintMediaType, bool useLandscapeOrientation)
        {
            await PrintToPdf(postedHtml, useLandscapeOrientation);
            return new FileInfo(postedHtml.FileName);
        }

        private async Task PrintToPdf(IFormFile postedHtml, bool useLandscapeOrientation)
        {
            var webView = new Microsoft.Web.WebView2.Wpf.WebView2
            {
                Source = new Uri("about:blank")
            };

            //// Initialize WebView2
            //await webView.EnsureCoreWebView2Async(null);
            //CoreWebView2PrintSettings printSettings = null;
            //if (useLandscapeOrientation)
            //{
            // can't find WebViewEnvironment
            //    printSettings = WebViewEnvironment.CreatePrintSettings();
            //    printSettings.Orientation =
            //        CoreWebView2PrintOrientation.Landscape;
            //}

            await webView.CoreWebView2.PrintToPdfAsync(
                postedHtml.FileName); //, printSettings);
        }

        public async Task<string> ReadAllText(IFormFile postedHtml)
        {
            byte[] buffer;
            using (var stream = postedHtml.OpenReadStream())
            {
                buffer = new byte[stream.Length];
                await stream.ReadAsync(buffer);
            }
            return Encoding.Default.GetString(buffer);
        }
    }
}

and I get a runtime error of Exception thrown: 'System.InvalidOperationException' in PresentationCore.dll Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware: Error: An unhandled exception has occurred while executing the request.

System.InvalidOperationException: The calling thread must be STA, because many UI components require this. at System.Windows.Input.InputManager..ctor() at System.Windows.Input.InputManager.GetCurrentInputManagerImpl() at System.Windows.Input.KeyboardNavigation..ctor() at System.Windows.FrameworkElement.FrameworkServices..ctor() at System.Windows.FrameworkElement.EnsureFrameworkServices() at System.Windows.FrameworkElement..ctor() at System.Windows.Interop.HwndHost..ctor() at Microsoft.Web.WebView2.Wpf.WebView2..ctor()

aligneddev commented 3 months ago

Rick Strahl published a blog post X post "IAC, the bottom line is this component lets you print HTML to PDF in any Windows execution environment including inside services and IIS"

It'd be really great to run this in a Linux container

releated feedback