Generating pdf from html using Prawn - ruby-on-rails

I want to generate pdf from html. A gem prawn seems to be the most popular one for that. I downloaded the manual for it, but there is no information about how to generate pdf from html.
In particular, I need it to work on Heroku also, but that's the second goal.
So how can I generate pdf from html using Prawn?

Look for Pdfkit, it's second most popular gem RubyToolbox. It generate PDF from HTML using wkhtmltopdf. On RailsCasts is one older tutorial.

This is a sample code to generate pdf from html using prawn
/// <summary>
/// Convert the HTML code from the specified URL to a PDF document
and send the document to the browser
/// </summary>
private void ConvertURLToPDF()
{
string urlToConvert = textBoxWebPageURL.Text.Trim();
// Create the PDF converter. Optionally the HTML viewer width can
be specified as parameter
// The default HTML viewer width is 1024 pixels.
PdfConverter pdfConverter = new PdfConverter();
// set the license key - required
pdfConverter.LicenseKey = "R8nYyNnI2MjRxtjI29nG2drG0dHR0Q==";
// set the converter options - optional
pdfConverter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
pdfConverter.PdfDocumentOptions.PdfCompressionLevel = PdfCompressionLevel.Normal;
pdfConverter.PdfDocumentOptions.PdfPageOrientation = PdfPageOrientation.Portrait;
// set if header and footer are shown in the PDF - optional - default
is false
pdfConverter.PdfDocumentOptions.ShowHeader = cbAddHeader.Checked;
pdfConverter.PdfDocumentOptions.ShowFooter = cbAddFooter.Checked;
// set if the HTML content is resized if necessary to fit the PDF
page width - default is true
pdfConverter.PdfDocumentOptions.FitWidth = cbFitWidth.Checked;
// set the embedded fonts option - optional - default is false
pdfConverter.PdfDocumentOptions.EmbedFonts = cbEmbedFonts.Checked;
// set the live HTTP links option - optional - default is true
pdfConverter.PdfDocumentOptions.LiveUrlsEnabled = cbLiveLinks.Checked;
// set if the JavaScript is enabled during conversion to a PDF - default
is true
pdfConverter.JavaScriptEnabled = cbClientScripts.Checked;
// set if the images in PDF are compressed with JPEG to reduce the
PDF document size - default is true
pdfConverter.PdfDocumentOptions.JpegCompressionEnabled = cbJpegCompression.Checked;
// enable auto-generated bookmarks for a specified list of HTML selectors
(e.g. H1 and H2)
if (cbBookmarks.Checked)
{
pdfConverter.PdfBookmarkOptions.HtmlElementSelectors = new string[] { "H1", "H2" };
}
// add HTML header
if (cbAddHeader.Checked)
AddHeader(pdfConverter);
// add HTML footer
if (cbAddFooter.Checked)
AddFooter(pdfConverter);
// Performs the conversion and get the pdf document bytes that can
// be saved to a file or sent as a browser response
byte[] pdfBytes = pdfConverter.GetPdfBytesFromUrl(urlToConvert);
// send the PDF document as a response to the browser for download
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.Clear();
response.AddHeader("Content-Type", "application/pdf");
if (radioAttachment.Checked)
response.AddHeader("Content-Disposition",
String.Format("attachment; filename=GettingStarted.pdf; size={0}",
pdfBytes.Length.ToString()));
else
response.AddHeader("Content-Disposition",
String.Format("inline; filename=GettingStarted.pdf; size={0}",
pdfBytes.Length.ToString()));
response.BinaryWrite(pdfBytes);
// Note: it is important to end the response, otherwise the ASP.NET
// web page will render its content to PDF document stream
response.End();
}

Related

How to attach a created file to mail mvc

As each user runs through my application I hold their data and dump it into a report as follows, which at the end is created into a pdf document and is later automatically downloaded on the users side(client-side). I now want to attach this document to an email and have it forwarded to them. This is where I have troubles with the attachment.
Code as follows:
ReportDocument rd = new ReportDocument();
rd.Load(Path.Combine(Server.MapPath("~/Reports/PP_RentalAgreement.rpt")));
rd.SetParameterValue("rent_agree_no", _1);
rd.SetParameterValue("r_initial", _2);
rd.SetParameterValue("r_f_name", _3);
rd.SetParameterValue("r_l_name", _4);
rd.SetParameterValue("r_id_no", _5);
rd.SetParameterValue("r_lic_no", _6);
rd.SetParameterValue("r_tel", _7);
rd.SetParameterValue("r_cell", _8);
rd.SetParameterValue("r_fax", _9);
Response.Buffer = false;
Response.ClearContent();
Response.ClearHeaders();
Stream st = rd.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat);
st.Seek(0, SeekOrigin.Begin);
if (ModelState.IsValid)
{
var m_message = new MailMessage();
m_message.To.Add(new MailAddress("JoeSoap#TextMail.com"));
m_message.Subject = "Pink Panther - Invoice";
m_message.Attachments.Add(new Attachment(st, "application/pdf", "Invoice.pdf"));
using (var smtp = new SmtpClient())
{
await smtp.SendMailAsync(m_message);
return RedirectToAction("Index");
}
}
I am getting an error on this line : m_message.Attachments.Add(new Attachment(st, "application/pdf", "Invoice.pdf")); saying The specified content type is invalid.
Someone suggested to me that I should specify a path however I am not actually saving this file anywhere
How am I able to allow the file to be attached and send it to the recipient?
The System.Net.Mail.Attachment class constructor with 3 overloads consist of these parameters:
public Attachment(System.IO.Stream contentStream, string name, string mediaType)
Hence, you're assigning name and content type in reversed order, which causing invalid content type problem at this code:
m_message.Attachments.Add(new Attachment(st, "application/pdf", "Invoice.pdf"));
The correct way is putting the file name as second argument like example below:
m_message.Attachments.Add(new Attachment(st, "Invoice.pdf", "application/pdf"));
Or using MediaTypeNames for content type setting:
m_message.Attachments.Add(new Attachment(st, "Invoice.pdf", MediaTypeNames.Application.Pdf));

Render Embedded Image in Email Body using Microsoft Graph or Outlook REST API

When we get email using Microsoft Graph/Outlook REST API it's body contains the references for embedded images like below.
<img src="cid:image001.jpg#1D3E60C.5A00BC30">
I am looking to find out a way so i can display the embedded images properly as the above image tag does not display any image. I have done some search but did not found any help on that.
Below is sample code for getting an email by id using Microsoft Graph API.
// Get the message.
Message message = await graphClient.Me.Messages[id].Request(requestOptions).WithUserAccount(ClaimsPrincipal.Current.ToGraphUserAccount()).GetAsync();
For getting attached resources with email using Microsoft Graph API you need to get email like below.
// Get the message with all attachments(Embedded or separately attached).
Message message = await graphClient.Me.Messages[id].Request(requestOptions).WithUserAccount(ClaimsPrincipal.Current.ToGraphUserAccount()).Expand("attachments").GetAsync();
Once you have all attachments with email detail you need to iterate through attachments list and check if attachment IsInline property is set as true then simply replace the
cid:image001.jpg#1D3E60C.5A00BC30
with Base64String created from bytes array of attachment.
string emailBody = message.Body.Content;
foreach (var attachment in message.Attachments)
{
if (attachment.IsInline.HasValue && attachment.IsInline.Value)
{
if ((attachment is FileAttachment) &&(attachment.ContentType.Contains("image")))
{
FileAttachment fileAttachment = attachment as FileAttachment;
byte[] contentBytes = fileAttachment.ContentBytes;
string imageContentIDToReplace = "cid:" + fileAttachment.ContentId;
emailBody = emailBody.Replace(imageContentIDToReplace,
String.Format("data:image;base64,{0}", Convert.ToBase64String(contentBytes as
byte[])));
}
}
}
Now render the email body using emailBody variable it will display all embedded images.
Use below code to display logo image on image tag using graph Api in C#.
var fileAttachment = new FileAttachment
{
ODataType = "#microsoft.graph.fileAttachment",
Name = Path.GetFileName(attachment),
ContentLocation = attachment,
ContentBytes = contentBytes,
ContentType = contentType,
ContentId= contentId,
IsInline = true
};
Note : Here IsInline = true must need to be added if you want to display image on image tag only not as attachment.

Uri is not supported when saving pdf in server folder with nreco pdf generator

I have the following code:
var htmlToPdf = new NReco.PdfGenerator.HtmlToPdfConverter();
htmlToPdf.PdfToolPath = "~/files/";
htmlToPdf.GeneratePdf(template);
Which throws the following error:
Uri is not supported when saving pdf in server folder with nreco pdf generator.
You will need to set a regular path to your file system like e.g. "C:\temp\myfolder\". Or use a . instead of ~ and backslashes:
htmlToPdf.PdfToolPath = ".\\files\\";
If NReco is able to deliver you an byte-array or a stream you should prefer this instead of a file and return it directly.
UPDATE:
After takeing a look into the documentation of NReco all you need to do is following:
var htmlToPdf = new NReco.PdfGenerator.HtmlToPdfConverter();
htmlToPdf.PdfToolPath = "<CORRECT_PATH_FOR_TOOL>";
var output = htmlToPdf.GeneratePdf(template);
System.IO.File.WriteAllBytes("<OUTPUT_PATH>", output);
This should create your pdf in the OUTPUT_PATH.
#OlaFW thanx for your effort.
I got my answer.
var pdfBytes = htmlToPdf.GeneratePdf(template);
string filePath = "/files/Myfile.pdf";
string Url = System.Web.Hosting.HostingEnvironment.MapPath(filePath);
System.IO.File.WriteAllBytes(Url, pdfBytes);

Vaadin File Uploading using FileFilter

I am Using Vaadin Framework. I need to Upload Files in the format of PDF,JAR & ZIP only. I tried with this code.This code is also I got from STACK OVER FLOW.
public void uploadStarted(StartedEvent event) {
// TODO Auto-generated method stub
System.out.println("***Upload: uploadStarted()");
ArrayList<String> allowedMimeTypes = new ArrayList<String>();
allowedMimeTypes.add("application/java-archive");
allowedMimeTypes.add("application/pdf");
allowedMimeTypes.add("application/zip");
String contentType = event.getMIMEType();
boolean allowed = false;
System.out.println(":::::::::::::contentType::::::"
+ contentType);
for (int i = 0; i < allowedMimeTypes.size(); i++) {
if (contentType.equalsIgnoreCase(allowedMimeTypes.get(i))) {
allowed = true;
break;
}
}
try {
if (allowed) {
System.out.println("boolean value:::::::allowed"
+ allowed);
finalDeedUpload.setReceiver(finalDeedFileUploadHandler);
finalDeedUpload.addListener(finalDeedFileUploadHandler);
} else {
showWarningNotification(
"Error:Please Upload File in Given Format", "");
}
This is working for while uploading PDf files it's working, while uploading Zip OR Jar file and any other file it is showing NULLPOINTER EXCEPTION.
Please help me.
Vaadin has a special upload component which is easy to use. There is a whole chapter in Book of Vaadin related to this component.
https://vaadin.com/book/-/page/components.upload.html
In Vaadin 14 there is a method setAcceptedFileTypes at class Upload:
MemoryBuffer buffer = new MemoryBuffer();
Upload upload = new Upload(buffer);
upload.setAcceptedFileTypes(new String[]{"application/zip", "application/pdf", "application/java-archive"});
The method setAcceptedFileTypes sets the HTML attribute accept at the <input type="file"> element and therefore limits / filters what the application user can upload.

base64 img src in WebUI leads to an error

Here is the problematic part of the template:
<ul id="list">
<template iterate='file in convertedfiles.files'>
<li>{{file.filename}}
<template if='file.isImage'>
<img src="{{file.src}}" alt="{{file.filename}}"><br/>
Source: {{file.src}}
</template>
</li>
</template>
</ul>
convertedfiles is a list of AndroidFile:
class AndroidFile {
File _file;
String filename;
String src;
bool isImage;
AndroidFile(this._file) : isImage = false {
filename = htmlEscape(_file.name);
// If the file is an image, read and display its thumbnail.
if (_file.type.startsWith('image')) {
FileReader reader = new FileReader();
reader.on.load.add((e) {
src = reader.result.toString().trim();
// prints the correct URL (data:image/png;base64,...)
print(src);
isImage = true;
watcher.dispatch();
});
reader.readAsDataUrl(_file);
}
}
}
The template gets displayed. It shows the filename, it shows the source but the imagetag looks like
<img alt="screenshot-1179.png" src="#">
The hash is underlined (in Chromium source view) and if I click on it it says "File not found: /web/out/"
Converted to JS is says in Chrome:
"Resource interpreted as Image but transferred with MIME type text/html"
Sample source is on GitHub
Any hints?
Note that if you know that you are handling a safe URI that is not vulnerable to XSS, you can work around this problem by using a SafeUri wrapper (imported from web_ui/web_ui.dart). For instance, change your template from:
<img src="{{file.src}}" alt="{{file.filename}}">
to:
<img src="{{new SafeUri.unsafe(file.src)}}" alt="{{file.filename}}">
Or change file.src internally to store a SafeUri.
I found the problem.
It's because the URI gets sanitized for security reasons. The sanitizer turns invalid URIs into a hash #.
From web_ui/templating.dart:
/**
* Ensure that [usiString] is a safe URI. Otherwise, return a '#' URL.
*
* The logic in this method was based on the GWT implementation located at:
* http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/gwt/safehtml/shared/UriUtils.java
*/
String sanitizeUri(uri) {
if (uri is SafeUri) return uri.toString();
uri = uri.toString();
return _isSafeUri(uri) ? uri : '#';
}
const _SAFE_SCHEMES = const ["http", "https", "ftp", "mailto"];
bool _isSafeUri(String uri) {
var scheme = new Uri(uri).scheme;
if (scheme == '') return true;
// There are two checks for mailto to correctly handle the Turkish locale.
// i -> to upper in Turkish locale -> İ
// I -> to lower in Turkish locale -> ı
// For details, see: http://www.i18nguy.com/unicode/turkish-i18n.html
return _SAFE_SCHEMES.contains(scheme.toLowerCase()) ||
"MAILTO" == scheme.toUpperCase();
}
So the sanitizer turns your data: scheme URI into a #. Data URIs can be used for XSS, but as far as I know the check could be improved by allowing data URIs when the data URI content type is image/*.
Perhaps file a bug report?

Resources