Blazor pages generate HTML to PDF - c#

I want to print a rendered modal that opens up with some vehicle information, The button is present on the modal and on click should convert the modal HTML to PDF.
Please advise if there is a C# function or so that I can use, which will extract the current HTML I want to convert to PDF, or steer me in the right direction. I have only been doing C# for about 2 months so lacking experience and expertise.
Edit: Also trying to get the code to use CSS when dumping the PDF.
I have added the following code.
References, etc. added.
#using BidWheels.Configuration;
#using BidWheels.Shared.Controls;
#using BidWheels.Data;
#using BidWheels.CustomProviders;
#using BidWheels.Services;
#using System.Timers;
#using Syncfusion.Pdf;
#using Syncfusion.HtmlConverter;
#using System.Linq;
#using System.Web;
#using Microsoft.AspNetCore.Http;
#inject AppState AppState;
#inject UsersDAL UsersDAL;
#inject MainDAL MainDAL;
#inject NotificationService NotificationService;
#inject GeneralConfiguration GeneralConfiguration;
#inject GlobalVar GlobalVar;
#inject GlobalVarShared GlobalVarShared;
#inject Microsoft.JSInterop.IJSRuntime JS
#inject IHttpContextAccessor httpContextAccessor
Button to proc the function to generate the PDF
</div>
<button class="btn btn-outline-success" #onclick="#CreatePDF">Generate PDF</button>
</div>
This is the function invoked by the button.
#functions {
void CreatePDF()
{
> //Initialize HTML to PDF converter
> //HtmlToPdfConverter htmlConverter = new HtmlToPdfConverter(HtmlRenderingEngine.WebKit);
> //WebKitConverterSettings settings = new WebKitConverterSettings();
> //Set WebKit path
> //settings.WebKitPath = Server.MapPath("~/QtBinaries");
> //Assign WebKit settings to HTML converter
> //htmlConverter.ConverterSettings = settings;
> //Get the current URL
> //string url = HttpContext;
> //Convert URL to PDF
> //PdfDocument document = htmlConverter.Convert(url);
> //Save the document
> //document.Save("Sample.pdf", HttpContext.Current.Response, HttpReadType.Save);
Code updated to below
HtmlToPdfConverter htmlConverter = new HtmlToPdfConverter();
WebKitConverterSettings webKitSettings = new WebKitConverterSettings();
webKitSettings.WebKitPath = Directory.GetCurrentDirectory() + "\\wwwroot" + #"\QtBinariesWindows\";
webKitSettings.MediaType = MediaType.Print;
webKitSettings.Orientation = PdfPageOrientation.Portrait;
htmlConverter.ConverterSettings = webKitSettings;
Convert HTML to PDF.
string baseUrl = #"" + Directory.GetCurrentDirectory() + "/wwwroot/css/";
string HTMLBody = await JS.InvokeAsync<string>("getHTMLtoRender", PDFBody);
PdfDocument pdfDocument = htmlConverter.Convert(HTMLBody, baseUrl);
MemoryStream memoryStream = new MemoryStream();
Save and close the document instance.
pdfDocument.Save(memoryStream);
JS.SaveAs("Sample.pdf", memoryStream.ToArray());
}
}
The following errors are being generated.
Error messages generated by the code for the above:
New errors generated and lib:

I always found programmatic PDF handling very difficult.
If you really need a programmatic way take a look at this: Convert HTML to PDF in .NET.
Otherwise I'd advise you to use the browsers print feature with a print to pdf tool, of wich at least one was usually already installed on every system i saw.

1st render razor component to html string as the following
var host = new TestHost();
var component = host.AddComponent<YourComponent>();
var html = component.GetMarkup();
then pass this string to IronPdf library to generate the pdf for you
using IronPdf;
var Renderer = new IronPdf.ChromePdfRenderer();
var PDF = Renderer.RenderHtmlAsPdf(html);
References
stackoverflow link How to render a Blazor component into an HTML string
IronPdf IronPdf website

Once you created the pdf as explained here you can use Append.Blazor.Printing to have a native print dialog. You can see the blogpost here.

Related

How to open a File stream in new tab in Blazor Web Assembly?

I have scenario, where i have a API, which will return FileStream(with Content Type application/pdf). On Button click i need to call the API & get the file stream & open the stream in a New Tab.
have tried "IJsRuntime". but below code seems not working. Is there any other way to handle this?
jsRuntime.InvokeVoidAsync("open", "File_Stream", "_blank")
I am new to Blazor.
I have tried from my side. Below approach worked for me.
In Razor page add required button or label & add the Onclick eventHandler code along with Injection with IjsRuntime.
#inject IJSRuntime JSRuntime
<button #onclick="GetFilecontent">Click Here</button>
#code {
private async Task GetFilecontent()
{
var result = await Http.GetAsync("api/getFileBytes"); //Output should be FileContent
var FileStream = result.Content.ReadAsByteArrayAsync().Result.ToArray();
await JSRuntime.InvokeVoidAsync("openInNewTab",FileStream);
}
}
openInNewTab is the Js function which will take the File stream & will render in a new tab with the File Array.
Create a HTML file , where the File will render.
<script src="_framework/blazor.webassembly.js"></script>
<script>
function openInNewTab(array) {
// Create a Blob object from the array
var file = new Blob([array], { type: 'application/pdf'});
// Create a URL for the Blob
var fileURL = URL.createObjectURL(file);
// Open the URL in a new tab
window.open(fileURL, '_blank');
}
</script>
File will open in new window.

How do I render a pdf in the browser generated from DynamicPDF Cloud API?

Using DynamicPDF's Cloud API, instead of generating a pdf back to the local file system, I would like it to directly open in another browser tab to be available for printing immediately. How do I accomplish that?
The method I am using (.NET Core 6 / Blazor) is below:
public async Task CallDynPDFCloudAPI()
{
var basePath = #"JSONFiles\";
var apiKey = "foo";
var cloudPath = "bar.dlex";
Pdf pdf = new Pdf();
pdf.ApiKey = apiKey;
LayoutDataResource layoutDataResource = new LayoutDataResource(basePath + "FooBar.json");
pdf.AddDlex(cloudPath, layoutDataResource);
PdfResponse pdfResponse = pdf.Process();
if (pdfResponse.IsSuccessful)
{
File.WriteAllBytes(basePath + "Manifest_" + manifestBranch + ".pdf", pdfResponse.Content);
}
else
{
Console.WriteLine(pdfResponse.ErrorJson);
}
}
Reread article on https://learn.microsoft.com/en-us/aspnet/core/blazor/file-downloads?view=aspnetcore-6.0
#page "/file-download-1"
#using System.IO
#inject IJSRuntime JS
<h1> File Download Example</h1>
<button #onclick = "DownloadFileFromStream" >
Download File From Stream
</button>
#code {
private Stream CallDynPDFCloudAPI()
{
var basePath = #"JSONFiles\";
var apiKey = "foo";
var cloudPath = "bar.dlex";
Pdf pdf = new Pdf();
pdf.ApiKey = apiKey;
LayoutDataResource layoutDataResource = new LayoutDataResource(basePath + "FooBar.json");
pdf.AddDlex(cloudPath, layoutDataResource);
PdfResponse pdfResponse = pdf.Process();
if (pdfResponse.IsSuccessful)
{
return new MemoryStream(pdfResponse.Content);
}
else
{
throw new Exception("");
}
}
private async Task DownloadFileFromStream()
{
var fileStream = CallDynPDFCloudAPI();
var fileName = "file.pdf";
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
}
You won't be able to access the PDF content for this request from another browser tab. I'd recommend opening the new tab before making the call and then streaming it there. If you're using an 'a href' link, you can accomplish this by setting the 'target="_blank"' property of the 'a href'. If this is a form submission, you can set the 'target="_blank"' property of the 'form'.
The other option would be to store the PDF somewhere temporarily (as a file, in a DB or in BLOB storage) then stream it to the other tab once it's opened.
Ive impletented #Mihal's answer, with modified code suggested by #DynamicPDF to achieve the result I was looking for. My two goals were:
Not clutter the client device with excessive dowloaded files
Not needing to save the file in Server or DB
Javascript:
<script>
window.downloadFileFromStream = async (fileName,
contentStreamReference) => {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
//--Opens PDF file in new Tab
fetch(url)
.then(response => response.blob())
.then(data => window.open(URL.createObjectURL(data), '_blank'))
//--Downloads file to Browser (uncomment if desired)
//const anchorElement = document.createElement('a');
//anchorElement.href = url;
//anchorElement.download = fileName ?? 'Manifest';
//anchorElement.click();
//anchorElement.remove();
//URL.revokeObjectURL(url);
}
</script>
*NOTE! My application is purely internal-facing to our organization. Our Windows client machines and browsers are managed by Group Policy. I have not yet tested this on Mac / Safari clients yet.

Add content to every view or component in ASP NET MVC

I'm using ASP .NET Core MVC (views and controllers).
Is there a way to add some additional output to all *.cshtml files using middleware, filter or something similar?
I would like to display the path(s) of all cshtml-files like the view itself, partial views, layout-file or components, that are part of the current page.
This is how it should look like:
Right now, I have to add this line to the *.cshtml files, one by one:
#using WkOne.AuthorizationServer.ViewModels;
#model IEnumerable<UserViewModel>
#{
Layout = "_Layout3Cols";
ViewData["Title"] = "Users";
}
<!-- I need this line in every cshtml file -->
<!-- \ -->
<div style="font-size: small;background-color: #CFC;">Path: #Path.ToString() </div>
<table class="table">
<!-- ... and so on... -->
But what I'm looking for is a way to do this in central place.
Any suggestions?
MVC project returns the html codes(razor codes has already been complied to html,so your codes shouldn't contain razor codes) which contained in response body to browser,
The response body could write but couldn't be read ,if you want to add the html codes somewhere you want ,I think you need to replace the default body
I tried as below and added the text "hi"
public class CusTestMiddleware
{
private readonly RequestDelegate _next;
public CusTestMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var response = context.Response;
var responseOriginalBody = response.Body;
using var memStream = new MemoryStream();
response.Body = memStream;
await _next(context);
var targetstr = "<a>hi</a>";
byte[] targetbyte = Encoding.UTF8.GetBytes(targetstr);
memStream.Write(targetbyte);
memStream.Position = 0;
var responseReader = new StreamReader(memStream);
var responseBody = await responseReader.ReadToEndAsync();
memStream.Position = 0;
await memStream.CopyToAsync(responseOriginalBody);
response.Body = responseOriginalBody;
}
}

C# Get data from website and show it in textbox

Hello i am pretty new in c# sphere. I want to make a little program that will fetch data from the given page.
It is a fragment of website:
<h3 class="filmInfo__header cloneToCast cloneToOtherInfo" data-type="directing-header">reżyseria</h3>
<div class="filmInfo__info cloneToCast cloneToOtherInfo" data-type="directing-info"> <span itemprop="url" content="/person/Rupert+Sanders-1121101"></span> <span itemprop="name">Rupert Sanders</span> </div>
I want to get data from "Data-type="Directing-info" and get a result from title="Rupert Sanders"
Somebody can help me ?
My very simple code:
private void button1_Click(object sender, EventArgs e)
{
var url = "https://www.filmweb.pl/film/Kr%C3%B3lewna+%C5%9Anie%C5%BCka+i+%C5%81owca-2012-600541";
var httpClient = new HttpClient();
var html = httpClient.GetStringAsync(url);
textBox1.Text = (html.Result);
}
C# or .NET does not offer native HTML parsing functionality. However, there are a handful of libraries which provides HTML parsing functionality. For example, you can use Html Agility Pack.
First, you need to install it into your project. You can easily install it with NuGet Package Manager in Visual Studio if you use it.
After that, you can use it like this with your input HTML:
private void button1_Click(object sender, EventArgs e)
{
var url = "https://www.filmweb.pl/film/Kr%C3%B3lewna+%C5%9Anie%C5%BCka+i+%C5%81owca-2012-600541";
var httpClient = new HttpClient();
var html = httpClient.GetStringAsync(url);
// Create a HtmlDocument and load your HTML into it.
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(html.Result);
// Find your desired node inside it.
HtmlNode directingInfoNode = htmlDocument.DocumentNode.SelectSingleNode("//div[#data-type='directing-info']/a");
// Get the title attribute of that node.
HtmlAttribute titleAttribute = directingInfoNode.Attributes["title"];
textBox1.Text = (titleAttribute.Value);
}
Of course, you will need to put necessary using statement to the top of your file:
using HtmlAgilityPack;

Render dynamic HTML with embedded Razor variables using MVC

I have some encoded Html which have any number of 1000s of different Razor variables embedded within it that I have stored and need to retrieve from the database. I want to be able to render this in a MVC/razor view.
Just one simple example of the html saved on the database (it can be more complex):
"<span>Your page is #Config.PageColour and you have page size of #Config.PageSize</span>"
MessageController.cs
public ActionResult ShowMessage()
{
var htmlToDisplay = _messageDAL.getHtmlMessage();
var messageVm = new MessageVm
{
DisplayMessage = htmlToDisplay;
};
return View("Index.cshtml", "", messageVm);
}
Index.cshtml
<html>
#Html.Raw(#model.DisplayMessage)
</html>
Results
When I run this the rendered page looks like this:
Your page is #Config.PageColour and you have page size of #Config.PageSize
But I want it to interpret the value of the Razor variable with the html block and should look like this:
Your page is Blue and you have page size of A4
Really stuck on this so any help would be appreciated!
Use this line. I hope this may help.
#Html.Raw(System.Web.HttpUtility.HtmlDecode(#model.DisplayMessage))
EDIT 1
You can use any Razor Compiler like the one mentioned below
RazorEngine:
string result = RazorEngine.Razor.Parse(#model.DisplayMessage, new { Name = "Name" });
RazorEngine does not support any of the Mvc helpers such as Html and Url. Since these libraries are supposed to exist outside of Mvc and thus require more work to get them to work with those helpers.**
EDIT 2
You can use a Razor compiler that allows you to use HTML templates called RazorEngine which can be found at https://github.com/Antaris/RazorEngine
From Visual Studio, using the Package Manager Console command:
Install-Package RazorEngine
After installation I changed my controller as follows:
MessageController.cs
public ActionResult ShowMessage()
{
var htmlTemplate = _messageDAL.getHtmlMessage();
var htmlToDisplay = Engine.Razor.RunCompile(htmlTemplate , "messageTemplateKey", null, new { Name = "some model data" });
var messageVm = new MessageVm
{
DisplayMessage = htmlToDisplay;
};
return View("Index.cshtml", "", messageVm);
}
You can use a Razor compiler that allows you to use HTML templates called RazorEngine which can be found at https://github.com/Antaris/RazorEngine
From Visual Studio, using the Package Manager Console command:
Install-Package RazorEngine
After installation I changed my controller as follows:
MessageController.cs
public ActionResult ShowMessage()
{
var htmlTemplate = _messageDAL.getHtmlMessage();
var htmlToDisplay = Engine.Razor.RunCompile(htmlTemplate , "messageTemplateKey", null, new { Name = "some model data" });
var messageVm = new MessageVm
{
DisplayMessage = htmlToDisplay;
};
return View("Index.cshtml", "", messageVm);
}
And it worked first time. Big thanks to #Mukesh Kumar who provided the vital clues to rewrite the code which I've posted as a complete and working answer here.

Categories

Resources