Show PDF in HTML in web - asp.net-mvc

I'm using the object tag to render PDF in HTML, but I'm doing it in MVC like this:
<object data="/JDLCustomer/GetPDFData?projID=<%=ViewData["ProjectID"]%>&folder=<%=ViewData["Folder"] %>"
type="application/pdf" width="960" height="900">
</object>
and Controller/Action is
public void GetPDFData(string projID, Project_Thin.Folders folder)
{
Highmark.BLL.Models.Project proj = GetProject(projID);
List<File> ff = proj.GetFiles(folder, false);
if (ff != null && ff.Count > 0 && ff.Where(p => p.FileExtension == "pdf").Count() > 0)
{
ff = ff.Where(p => p.FileExtension == "pdf").ToList();
Response.ClearHeaders();
Highmark.BLL.PDF.JDLCustomerPDF pdfObj = new JDLCustomerPDF(ff, proj.SimpleDbID);
byte[] bArr = pdfObj.GetPDF(Response.OutputStream);
pdfObj = null;
Response.ContentType = "application/" + System.IO.Path.GetExtension("TakeOffPlans").Replace(".", "");
Response.AddHeader("Content-disposition", "attachment; filename=\"TakeOffPlans\"");
Response.BinaryWrite(bArr);
Response.Flush();
}
}
The problem is, as I'm downloading data first from server and then return the byte data, it is taking some time in downloading, so I want to show some kind of progress to show processing.
Please help me on this.

You may try the following (not tested under all browsers):
<div style="background: transparent url(progress.gif) no-repeat">
<object
data="<%= Url.Action("GetPDFData, new { projID = ViewData["ProjectID"], folder = ViewData["Folder"] }") %>"
type="application/pdf"
width="640"
height="480">
<param value="transparent" name="wmode"/>
</object>
</div>

Unfortunatly, there is no way (afaik) to interact with the Acrobat plugin and see when it's ready to display your PDF document.
There are components available that replace Acrobat and provide a proper Javascript interface.
I work for TallComponents on their PDFWebViewer.NET product which will display PDF without any plugins and works with ASP.NET MVC.
You do have some other options though. If you need the progress indicator because the PDF generation is taking longer than you would like you can poll the server for progress using AJAX calls.
On the server you would need to have some sort of progress information available that you can return as the result of the ajax call. In the browser you'd use the result to provide progress info to the user. There are several good examples available online (this blog for example). There are also other questions here on SO (for example here) with good pointers to more info.
If the generation process only takes a couple of seconds can you probably get way with showing a busy indicator. That could be as simple as showing a div in your page when you trigger the download from the server.
By the way, if I'm not mistaken you should replace the attachment keyword with inline in the Content-Disposition header. Setting that to attachment will cause the entire PDF to be downloaded before any content is displayed. If you set it to inline, Acrobat will start showing the first page as soon as it has downloaded enough data to do so.

Related

pdf.js rendering as PDF with base64

I am stuck at last point of my application, i am supposed to display user form in PDF which works fine on desktop browsers as they has pdf viewer built in, but for Android / iOS its not working as pdf viewer is missing.
So i was trying to use PDF.js to display it, (to be honest, this is very widely used but documentation is lacking), only catch is i am getting data in base64 format. PDF.js has example on site which shows how to render the base64 data but its not PDF, for that displaying PDF as "PDF" i need to user their "viewer.html" but that does not take base64 data?
closest i have come to Pdf.js: rendering a pdf file using base64... on stack overflow, but i dont know how to use it after PDFJS.getDocument(pdfAsArray)?.
Other link that came across was other link
I dont want to rely on Google / Third party PDF viewer as i dont know how long they will support this.
There are no end-to-end answers on this topic in community so here is my attempt to put something here. (maybe it will help others)
Okay, PDF.js is one way of showing PDF in browser, specially when you don't want to rely on PDF plugin to be installed. In my case, my application generates report in PDF and that can be viewed before downloading but on handheld devices it was not working because of missing PDF viewer plugin.
In my case PDF was sent to browse in base64 string, that I can use to display PDF with <object src="base64-data"...></object>. This works like charm on Chrome / FF but switch to mobile view and it stops working.
<object type="application/pdf" id="pdfbin" width="100%" height="100%" title="Report.pdf">
<p class="text-center">Looks like there is no PDF viewer plugin installed, try one of the below approach...</p>
</object>
In above code it will try to show the PDF or fall back to <p> and show error message. And I Was planning to add the PDF viewer at this point, PDF.js was the choice but was not able to display it. One example on PDF.js with Base64 data shows how to do this but that renders it as an Image not PDF, and I was not able to find solution for that and hence the question, here is what I did,
First add the JavaScript code to convert base64 to array
convert to blob and use viewer.html file packaged with PDF.js to display it as PDF
In case if you are wondering why base64 data, then answer is simple I can create the PDF, read it, send the data to client and delete the file, I don't have to run any cleaner service/cron job to delete generated PDF files
Few Things To Note
Below code is using Flask + Jinja2, change the way base64 is read in html if you are using something else
viewer.html needs to be changed to have required js & css files in proper location (by default their location is relative; you need them to be referred from static folder)
viewer.js looks for pdf.worker.js in predefined location, change that in case its throwing error as above file not found.
viewer.js might throw file origin does not match viewer error in that case as a quick fix comment the code which throws this error and see if that solves the issue (look for that error in viewer.js)
I am not the author of below code, I have just put it together from different places.
Now to the code (so PDF will be displayed when user clicks on button with id="open_id")
Jquery
var pdfDataX = '{{ base64Pdf }}';
var BASE64_MARKER = ';base64,';
PDFJS.workerSrc = "{{ url_for('static', filename='js/pdf.worker.js') }}";
$('#open_id').click(function() {
PDFJS.disableWorker = true;
var pdfAsDataUri = "data:application/pdf;base64," + pdfDataX ;
PDFJS.workerSrc = "{{ url_for('static', filename='js/pdf.worker.js') }}";
// Try to show in the viewer.html
var blob = base64toBlob(pdfDataX, 'application/pdf');
var url = URL.createObjectURL(blob);
var viewerUrl = "{{ url_for('static', filename='viewer.html') }}" + '?file=' + encodeURIComponent(url);
$('#pdfViewer').attr('src', viewerUrl);
// Finish
var mdObj = $('#pdfbin');
mdObj.hide();
mdObj.attr('data', pdfAsDataUri);
mdObj.show();
$('#myModal').modal();
});
var base64toBlob = function(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i=0; i<slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
$('.save').click(function(e) {
e.preventDefault();
var blob = base64toBlob(pdfDataX, 'application/pdf');
saveAs(blob, 'abcd.pdf'); // requires https://github.com/eligrey/FileSaver.js/
return false;
});
HTML
<object type="application/pdf" id="pdfbin" width="100%" height="100%" title="Resume.pdf">
<p class="text-center">Looks like there is no PDF viewer plugin installed, try one of the below approach...</p>
<iframe id="pdfViewer" style="width: 100%; height: 100%;" allowfullscreen="" webkitallowfullscreen=""></iframe>
</object>
Hope this will be useful for others in future.

How to show umbraco multiple media picker images on page with a macro

Hello stackoverflow people
hope you can help me with maybe a simple question, but couldn't find a solution elsewhere and I have just been working with umbraco for a week now and have never used the mvc part before so all is new for me.
So the big problem is how I make a macro to show these images I choose from the multiple media picker the macro should just end with showing.
<img src="img1.gif" height="50" width="50">
<img src="img2.gif" height="50" width="50">
And so on depending on how many images there is. (the size is just an exempel)
I tryed somthing like this
#var selectedMedia3 = #Library.MediaById(Model.mainImage);
<img src="#selectedMedia3.umbracoFile" width="#selectedMedia3.umbracoWidth" height="#selectedMedia3.umbracoHeight" alt="#selectedMedia3.Name"/>
}
But I dont know how to parse the id of the image to the macro.
and when I choose more than one file I need a loop, but dont know how to loop the multiple media picker data, so im a little lost by now.
Are you able to let us know what version of Umbraco you are using. Umbraco has gone through a number of fundemental changes in various version over recent years. The below code should guide you in the right direction for Umbraco 7 Multiple Image picker with the propertyAlias partnersLogos.
#if (Model.Content.HasValue("partnersLogos"))
{
var partnersImagesList = Model.Content.GetPropertyValue<string>("partnersLogos").Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse);
var partnersImagesCollection = Umbraco.TypedMedia(partnersImagesList).Where(x => x != null);
foreach (var partnerImage in partnersImagesCollection)
{
<img src="#partnerImage.Url" alt="partners logo" />
}
}
If anyone makes the same mistake as me and doesn't realise that there is a difference between the now obsolete media picker and the new media picker "Umbraco.MediaPicker2" (true from at least 7.6.1) then please read the documentation on Umbraco's web site.
https://our.umbraco.org/documentation/Getting-Started/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker2
#{
var typedMultiMediaPicker = Model.Content.GetPropertyValue<IEnumerable<IPublishedContent>>("sliders");
foreach (var item in typedMultiMediaPicker)
{
<img src="#item.Url" style="width:200px"/>
}
}
I'm not really sure if you question is how to setup MVC within umbraco or getting values from an image picker.
But if you want to start up with MVC in umbraco check this out: http://24days.in/umbraco/2013/creating-reusable-code-in-mvc-apps/

Google drive embed NO iframe

Is there any way to embed google drive video without using iframes?
Just like you can do with youtube video:
<object width="320" height="180">
<param name="movie" value="http://www.youtube.com/v/UHk6wFNDA5s&showinfo=0">
<param name="wmode" value="transparent">
<embed src="http://www.youtube.com/v/UHk6wFNDA5s&showinfo=0" type="application/x-shockwave-flash" wmode="transparent" width="320" height="180">
</object>
The suggested embed code from google docs (using iframe) is:
<iframe src="https://docs.google.com/file/d/0B7CQ5XvLuIGrQlJUNUhpQVltZ0U/preview" width="640" height="385"></iframe>
It's possible but not officially supported.
After some study of the result generated by the iframe embed from Google Drive and the iframe from YouTube I've digged into the YouTube JS Player API and found out that it's possible using SWFObject embed
Here is the code that I use to add the player object:
function YT_createPlayer(divId, videoId) {
var params = {
allowScriptAccess: "always"
};
var atts = {
id: videoId
};
//Build the player URL SIMILAR to the one specified by the YouTube JS Player API
var videoURL = '';
videoURL += 'https://video.google.com/get_player?wmode=opaque&ps=docs&partnerid=30'; //Basic URL to the Player
videoURL += '&docid=' + videoId; //Specify the fileID ofthe file to show
videoURL += '&enablejsapi=1'; //Enable Youtube Js API to interact with the video editor
videoURL += '&playerapiid=' + videoId; //Give the video player the same name as the video for future reference
videoURL += '&cc_load_policy=0'; //No caption on this video (not supported for Google Drive Videos)
swfobject.embedSWF(videoURL,divId, widthVideo, heightVideo, "8", null, null, null, null);
}
You need to fetch the fileId from Google Drive some how (JS or server side, you can use a GAS Servlet if you want to host the site on Google Drive).
Most of the YouTube Player Parameters works, and events to control the playing status from JS are fired; so basically anything from the Youtube Documentation works.
Do you mean like this:
<object width="420" height="315" data="https://docs.google.com/file/d/0B7CQ5XvLuIGrQlJUNUhpQVltZ0U/preview">
<embed width="420" height="315" src="https://docs.google.com/file/d/0B7CQ5XvLuIGrQlJUNUhpQVltZ0U/preview">
I have tested the code and it works.
I used the example code from W3Schools, but cannot paste the code here as it is their copyright, just follow the link to see it.
The second part is to get the link to the images correct. I found using the link directly from Drive didn't work, so I created a new page in my site and added all the images I wanted. I disabled the page from navigation so it wouldn't be found unless they use search. After publishing, I opened the page and used Inspect on the browser to find the tag for each image. I then copied the element which looked like this.
<img src="https://lh3.googleusercontent.com/qt ... 4jM=w1175" class="CENy8b" role="img" style="width: 100%; margin: 0%">
I added the title attribute to this, so it is possible to see which images are included. I also removed the class="CENy8b" attribute as it doesn't seem to be required.
<img src="https://lh3.googleusercontent.com/qt ... 4jM=w1175" role="img" style="width: 100%; margin: 0%" title = "image 1">
I then pasted this over the tag in the code from W3Schools, repeating for each of the images. The W3Schools code has a where they have dots under the images to show which image from the set is being displayed. The number of dots needs to match the number of images.
Having done all the above I copied the code from the editor and used Embed code on Sites to paste it in. You can see the images ticking over in the Sites editor and after publishing it works fine on the live page.
The W3Schools code uses a 2 s delay between images. It is fairly easy to find where this is set in the code to change it to an appropriate value for your site.

display photos in grails vs playframework

I have been doing some tests using Grails framework and now I'm trying to do something similar in playframework.
Basically, I want to display some pictures, but to hide (in order to be able to avoid any crawling and to be able to change the hosting ) the pictures path.
The gsp page:
<g:each in="${images}" var="img">
<img class="thumbnail" src='${createLink(controller: "images", action: "displayImage", params:[img: img.name])}'/>
</g:each>
The controller:
def displayImage() {
File image = new File(IMAGES_DIR.absolutePath +'/' + params.img)
if(!image.exists()) {
response.status = 404
} else {
response.setContentType("application/jpg")
OutputStream out = response.getOutputStream();
out.write(image.bytes);
out.close();
}
}
The html generated page it looks like:
<img class="thumbnail" src='/myhost/images/displayImage?img=blabla.jpg' />
My questions:
Is this a best way to do it ?
Regarding the performance ?
Is this slower than juste displaying the pictures using http ?
Can I do something like this in Playframework ? If yes, how ?
Thanks.
C.C.
For static and public resources most probably using raw HTTP server will be fastest solution, so I don't think it's required to "drag" it through Java controller. Actually you can do it with Play very similar, but even easier - as Play allows yo to return a File as a response body directly ie (written from top of my head):
public static Result displayImage(String imagePath) {
File image = new File(SOME_CONFIGURED_FOLDER_WITH_IMAGES +'/' + imagePath)
if(!image.exists()) return notFound();
return ok(image).as("image/jpg");
}
Anyway, you should use it only if:
You are not gonna to use additional HTTP server (remember, that Play has built in one?)
You need to bring some access control
You want to perform some operations, ie. scaling, cropping etc. (in such case, IMHO it's also better to use Play only for creating the thumbnail, and serve it with common HTTP server...)
Thank's to this approach:
You don't waste processor's resources, as HTTP server just need to serve the file which is stored on disk, instead of rewriting it to the Result.
Your app can concentrate on other dynamic operations so it's faster.
You can (and should) use typical webmaster's techniques for optimizing serving static contents, like cookie free domains, advanced caching headers etc. (ofc you can do that also within Play controller, but...)

Print all steps of asp:Wizard control

I have a asp:Wizard control in my Web Application.I need to be able to print at any step within the wizard , and print all steps up to that step not just the current step.
I've added a print button to every step page , and tried to call the javascript:window.Print(), but only the current step gets printed.
How do i get all the steps to print in 1 page?
i'd like to try and get this working in javascript first before i go down the PDF route . I've tried doing somehting like this :
protected void Page_Load(object sender, EventArgs e)
{
StringWriter sw = new StringWriter();
HtmlTextWriter tw = new HtmlTextWriter(sw);
this.WizardStep2.RenderControl(tw);
string wizardHtmlContent = sw.ToString().Replace("\r\n", "");
string printScript = #"function printDiv(printpage)
{
var headstr = '<html><head><title></title></head><body>';
var footstr = '</body>';
var newstr = printpage;
var oldstr = document.body.innerHTML;
document.body.innerHTML = headstr+newstr+footstr;
window.print();
document.body.innerHTML = oldstr;
return false;
}";
this.Page.ClientScript.RegisterStartupScript(this.GetType(), "PrentDiv", printScript, true);
this.Button1.Attributes.Add("onclick", "printDiv('" + wizardHtmlContent + "');");
}
and for the aspx:
<form id="form1" runat="server">
<div>
<asp:Wizard ID="Wizard1" runat="server">
<WizardSteps>
<asp:WizardStep ID="WizardStep1" runat="server" Title="Step 1">
step1
</asp:WizardStep>
<asp:WizardStep ID="WizardStep2" runat="server" Title="Step 2">
step2
</asp:WizardStep>
</WizardSteps>
</asp:Wizard>
<asp:Button ID="Button1" runat="server" Text="Button" />
</div>
</form>
But i'm getting a missing runat=server error on line 3 , when i attempt to render the wizard control , so i think i may need to create a new window, then output the string before i print it , but cant seem to get that working ...Anyone any ideas ?
i have found a solution for my problem , i didnt manage to accomplish it client side , but ive managed to solve it server side which is better than going down the PDF route which i didnt want to do.
I found a great article here :
Printing in ASP.NET
which i ammended to print all steps of my wizard control in one go. thanks for all your help.
The javascript print method you're already using will work if you put the wizard steps in to a single page so they all render ...
the other way I guess is to simply browse to each step and hit your print button.
the way I would do it is use something like pdfsharp and give it the markup generates by each step and tell it to create a pdf page for each steps worth of markup ... from there the user has a pdf doc which they can simply view save or print using their usual pdf viewer.
The problem is that the javascript method is using a dom based api call to ask the browser to print the page which of course ultimately means you can only print the wizard step you're currently looking at ... using the pdf method means the user can preview the expected print out before printing and you have more control over what's printed.
It does require a bit more code though ...
pdfsharp can be found here: http://www.pdfsharp.net/Downloads.ashx
As you can see its free and open source.

Resources