I'm trying to create a PDF using jsPDF and HTML2Canvas.
I have multiple DIVs to insert into the PDF.
If I try to put all DIVs into a container and render once then it only puts the first page height into the PDF.
Can't figure out how to render multiple divs and stick them in the same PDF so that it keeps going page by page.
JAVASCRIPT
function genPDF() {
html2canvas(document.getElementById("container"), {
onrendered: function (canvas) {
var img = canvas.toDataURL();
var doc = new jsPDF();
doc.addImage(img, 'PNG');
doc.addPage();
doc.save('test.pdf');
}
});
}
HTML
<div id="container">
<div class="divEl" id="div1">Hi <img src="img1.JPG"> </div>
<div class="divEl" id="div2">Why <img src="img2.PNG"> </div>
</div>
<button onClick="genPDF()"> Click Me </button>
Add each of your images separately.
You need to wait for all the html2canvas renderings are done and added to pdf and then save your final pdf.
One way to achieve this by using JQuery and array of promises, actual code would look like this:
function genPDF() {
var deferreds = [];
var doc = new jsPDF();
for (let i = 0; i < numberOfInnerDivs; i++) {
var deferred = $.Deferred();
deferreds.push(deferred.promise());
generateCanvas(i, doc, deferred);
}
$.when.apply($, deferreds).then(function () { // executes after adding all images
doc.save('test.pdf');
});
}
function generateCanvas(i, doc, deferred){
html2canvas(document.getElementById("div" + i), {
onrendered: function (canvas) {
var img = canvas.toDataURL();
doc.addImage(img, 'PNG');
doc.addPage();
deferred.resolve();
}
});
}
For me it works like that:
$('#cmd2').click(function () {
var len = 4; //$x(".//body/div/div").length
var pdf = new jsPDF('p', 'mm','a4');
var position = 0;
Hide
for (let i = 1;i <= len; i++){
html2canvas(document.querySelector('#pg'+i),
{dpi: 300, // Set to 300 DPI
scale: 1 // Adjusts your resolution
}).then(canvas => {
pdf.addImage(canvas.toDataURL("images/png", 1), 'PNG', 0,position, 210, 295);
if (i == len){
pdf.save('sample-file.pdf');
}else{
pdf.addPage();
}
});
}
});
You could try this, using the follwing javascript references in the HTML. Html2Canvas and jsPDF are quite picky with versions you use.
https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.js"
https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.0.272/jspdf.debug.js"
var forPDF = document.querySelectorAll(".js-panel-pdf");
var len = forPDF.length;
var thisPDF = new jsPDF('p', 'mm', [240, 210]); //210mm wide and 297mm high
for (var i = 0; i < forPDF.length; i++) {
html2canvas(forPDF[i], {
onrendered: function(canvas) {
thisPDF.addImage(canvas.toDataURL("images/png", 1), 'PNG', 0, 0, 210, 295);
if (parseInt(i + 1) === len) {
thisPDF.save('sample-file.pdf');
} else {
thisPDF.addPage();
}
}
});
}
Stuart Smith - This is not working it does not allow to download the pdf
here is java script code
function generatePDF(){
var imgData;
var forPDF = document.querySelectorAll(".summary");
var len `enter code here`= forPDF.length;
var doc =new jsPDF('p','pt','a4');
for (var i = 0; i < forPDF.length; i++){
html2canvas(forPDF[i],{
useCORS:true,
onrendered:function(canvas){
imgData = canvas.toDataURL('image/jpg',1.0);
var doc =new jsPDF('p','pt','a4');
doc.addImage(imgData,'jpg',10,10,500,480);
if (parseInt(i + 1) === len) {
doc.save('sample-file.pdf');
} else {
doc.addPage();
}
}
});
}
}
Related
I use Amit Agarwal code from https://www.labnol.org/internet/light-youtube-embeds/27941/ in my website to lite embeded youtube video.
Is there any way to add start time to the script so that I could load each video with different start time.
<div class="youtube-player" data-id="VIDEO_ID" start-id="TIME"></div>
His complete script are as follows:
<script>
function labnolIframe(div) {
var iframe = document.createElement('iframe');
iframe.setAttribute('src', 'https://www.youtube.com/embed/' + div.dataset.id + '?autoplay=1&rel=0');
iframe.setAttribute('frameborder', '0');
iframe.setAttribute('allowfullscreen', '1');
iframe.setAttribute('allow', 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture');
div.parentNode.replaceChild(iframe, div);
}
function initYouTubeVideos() {
var playerElements = document.getElementsByClassName('youtube-player');
for (var n = 0; n < playerElements.length; n++) {
var videoId = playerElements[n].dataset.id;
var div = document.createElement('div');
div.setAttribute('data-id', videoId);
var thumbNode = document.createElement('img');
thumbNode.src='//i.ytimg.com/vi/ID/hqdefault.jpg'.replace('ID', videoId);
div.appendChild(thumbNode);
var playButton = document.createElement('div');
playButton.setAttribute('class', 'play');
div.appendChild(playButton);
div.onclick = function () {
labnolIframe(this);
};
playerElements[n].appendChild(div);
}
}
document.addEventListener('DOMContentLoaded', initYouTubeVideos);
</script>
I have added timeId var into the mix hoping that I could use start-id value to let embed video to start at specific time. It still start at 0 sec of the video.
function labnolIframe(div) {
var iframe = document.createElement('iframe');
iframe.setAttribute('src', 'https://www.youtube.com/embed/' + div.dataset.id + '?start='+ div.dataset.id + '&autoplay=1&rel=0');
iframe.setAttribute('frameborder', '0');
iframe.setAttribute('allowfullscreen', '1');
iframe.setAttribute('allow', 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture');
div.parentNode.replaceChild(iframe, div);
}
function initYouTubeVideos() {
var playerElements = document.getElementsByClassName('youtube-player');
for (var n = 0; n < playerElements.length; n++) {
var videoId = playerElements[n].dataset.id;
var timeId = playerElements[n].dataset.id;
var div = document.createElement('div');
div.setAttribute('data-id', videoId);
div.setAttribute('start-id', timeId);
var thumbNode = document.createElement('img');
thumbNode.src = '//i.ytimg.com/vi/ID/hqdefault.jpg'.replace('ID', videoId);
div.appendChild(thumbNode);
var playButton = document.createElement('div');
playButton.setAttribute('class', 'play');
div.appendChild(playButton);
div.onclick = function () {
labnolIframe(this);
};
playerElements[n].appendChild(div);
}
}
document.addEventListener('DOMContentLoaded', initYouTubeVideos);
Just change:
iframe.setAttribute('src', 'https://www.youtube.com/embed/' + div.dataset.id + '?autoplay=1&rel=0');
to:
iframe.setAttribute('src', 'https://www.youtube.com/embed/' + div.dataset.id + '?start=YOUR_START_TIME_IN_SECONDS&autoplay=1&rel=0');
Note the added start=YOUR_START_TIME_IN_SECONDS& in the URL.
I have one problem. addEventListener only works with the last element of the loop. I know what is the problem, but I can't figure it out. I get the JSON object from another function with the information. Later on the left side there should be clickable pictures. After clicking it I should get the same picture on the right side showed. Still it works only with the last one.
function myFunction(obj) {
var listItems = document.getElementsByClassName("newimg");
for (var i = 0; i < obj.length; i++) {
(function (i) {
document.getElementById("imgSmall").innerHTML += `<br></br><img id="${i}" class="newimg" src=${obj[i].download_url} >`;
let p = obj[i];
listItems[i].addEventListener('click', function() { makeithappen(p);},true);
}(i));
//obj[i].width,obj[i].height,obj[i].author,obj[i].download_url>
}
}
function makeithappen(k) {
document.getElementById("imgLarge").innerHTML = `<br class="text"> AUTHOR: ${k.author}, WIDTH: ${k.width}, HEIGHT: ${k.height}</br><img class="img2" src=${k.download_url} >`;
}
For quick fix.
Replace in your code
listItems[i].addEventListener('click', function() { makeithappen(p);},true);
with
listItems[i].onload = function() {
listItems[i].addEventListener('click', function () { makeithappen(p); }, true);
}
So when you got your listItems you weren't finished with the creation of more images. So new image means new list.
for (let i = 0; i < obj.length; i++) {
document.getElementById("imgSmall").innerHTML += `<br></br><img id="${i}" class="newimg" src=${obj[i].download_url}>`;
const listItems = document.getElementsByClassName("newimg");
listItems[i].addEventListener('click', function () { makeithappen(p); }, true);
}
function makeithappen(k) {
document.getElementById("imgLarge").innerHTML = `<br class="text"> AUTHOR: ${k.author}, WIDTH: ${k.width}, HEIGHT: ${k.height}</br><img class="img2" src=${k.download_url} >`;
}
Pleas do refactor <br></br> into something with css, margin or padding or whatever. This will then allow you to create the images with let div = document.createElement('img') and bind the event listener directly div.addEventlistener(...)
Hello I am creating a application using office.js which will be used in excel and word application addIn and I have written some code that actually gives the text of entire row cell by cell. but as my requirement was to maintain styles and of every cell and store them in database so that when again addin runs it should load the data in same format it was stored. Currently it is just text i am getting in response. I have asked a similar question like this which was to get the text with styles from current cell that really works great.
How do I get the formatting the Current cell of the table in Word using office.js
There is another thing if it is possible to get the cell html by row and column position that will also solve the problem.
Thank you!
Hi i found the solution of my problem but this is solution for word only this is not working in excel but this was working for me so i am writing here :-
function Addtable() {
var document = Office.context.document;
var headers = [["Cities"]];
var rows = [['<b>Hello there</b> <ul><li>One</li><li>Two</li></ul>'], ['Roma'], ['Tokyo'], ['Seattle']];
var html = '<table>';
html += '<thead>';
for (var i = 0; i < headers.length; i++) {
html += '<tr>';
var cells = headers[i];
for (var j = 0; j < cells.length; j++) {
html += '<th>' + cells[j] + '</th>';
}
html += '</tr>';
}
html += '</tr>';
html += '</thead>';
html += '<tbody>';
for (var i = 0; i < rows.length; i++) {
html += '<tr>';
var cells = rows[i];
for (var j = 0; j < cells.length; j++) {
html += '<td>' + cells[j] + '</td>';
}
html += '</tr>';
}
html += '</tbody>';
html += '</table>';
Office.context.document.setSelectedDataAsync(html, { coercionType: Office.CoercionType.Html }, function (asyncResult) {
document.bindings.addFromSelectionAsync(Office.BindingType.Table, function (result) {
console.log(result);
var binding = result.value;
binding.addHandlerAsync(Office.EventType.BindingSelectionChanged, onBindingSelectionChanged);
});
});
}
The above is the function that i call when i want to generate a table with html values in it.
and bellow is is code that i am using to get the value of the current cell and replacing with some dummy value.
var onBindingSelectionChanged = function (results) {
if (!isExecuted) {
Word.run(function (context) {
var tableCell = context.document.getSelection().parentTableCell;
context.load(tableCell);
return context.sync()
.then(function () {
if (tableCell.isNull == true) {
//selection is not within a cell.....
console.log("selection not in a header");
}
else {
// the selection is inside a cell! lets get the content....
var body = tableCell.body;
var html = tableCell.body.getHtml();
var tableHtml = tableCell.body.getHtml();
context.sync()
.then(function () {
var cellHtml = html.value;
var newHtml = "<table><tr><td><ul><li>yellow</li></ul></td></tr></table";
// Option 1
body.insertHtml(newHtml, Word.InsertLocation.replace);
// Option 2
//body.clear();
//body.insertHtml(newHtml, Word.InsertLocation.end);
return context.sync().then(function () {
console.log('HTML was successfully replaced.');
}).catch(function (e) {
console.log(e.message);
});
}).catch(function (e) {
console.log(e.message);
});
}
}).catch(function (e) {
console.log(e.message);
});
});
isExecuted = true;
}
else {
isExecuted=false;
}
};
Thank you!
This seems like it should be very standard behavior.
I can display a scrollable PDF with:
var container = document.getElementById('viewerContainer');
var pdfViewer = new PDFJS.PDFViewer({
container: container,
});
PDFJS.getDocument(DEFAULT_URL).then(function (pdfDocument) {
pdfViewer.setDocument(pdfDocument);
});
and I can display the PDF page by page with something like:
PDFJS.getDocument(URL_ANNOTATED_PDF_EXAMPLE).then(function getPdfHelloWorld(pdf) {
pdf.getPage(pageNumber).then(function getPageHelloWorld(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
But can't seem to find any reference in the API to both allow scrolling and jumping to a particular page, besides:
pdfViewer.currentPageNumber = 3;
which doesn't work...
So I found a way to make this work (mixed with a little Angular code, "$scope.$watch...") I now have other problems with font decoding. But here is a solution that might help someone else.
var me = this;
PDFJS.externalLinkTarget = PDFJS.LinkTarget.BLANK;
var container = document.getElementById('capso-court-document__container');
function renderPDF(url, container) {
function renderPage(page) {
var SCALE = 1;
var pdfPageView = new PDFJS.PDFPageView({
container: container,
id: page.pageIndex + 1,
scale: SCALE,
defaultViewport: page.getViewport(SCALE),
textLayerFactory: new PDFJS.DefaultTextLayerFactory(),
annotationLayerFactory: new PDFJS.DefaultAnnotationLayerFactory()
});
pdfPageView.setPdfPage(page);
return pdfPageView.draw();
}
function renderPages(pdfDoc) {
var pageLoadPromises = [];
for (var num = 1; num <= pdfDoc.numPages; num++) {
pageLoadPromises.push(pdfDoc.getPage(num).then(renderPage));
}
return $q.all(pageLoadPromises);
}
PDFJS.disableWorker = true;
return PDFJS.getDocument(url)
.then(renderPages);
}
$scope.$watch(function() {
return {
filingUrl: me.filingUrl,
whenPageSelected: me.whenPageSelected,
};
}, function(newVal, oldVal) {
if (newVal.filingUrl) {
//newVal.filingUrl = URL_EXAMPLE_PDF_ANNOTATED;
//newVal.filingUrl = URL_EXAMPLE_PDF_ANNOTATED_2;
//newVal.filingUrl = URL_EXAMPLE_PDF_MULTI_PAGE;
if (newVal.filingUrl !== oldVal.filingUrl &&
newVal.whenPageSelected &&
newVal.whenPageSelected.page) {
scrollToPage(newVal.whenPageSelected.page);
}
//HACK - create new container for each newly displayed PDF
container.innerHTML = '';
var newContainerForNewPdfSelection = document.createElement('div');
container.appendChild(newContainerForNewPdfSelection);
renderPDF(newVal.filingUrl, newContainerForNewPdfSelection).then(function() {
if (newVal.whenPageSelected &&
newVal.whenPageSelected.page) {
scrollToPage(newVal.whenPageSelected.page);
}
});
}
}, true);
function scrollToPage(pageNumber) {
var pageContainer = document.getElementById('pageContainer' + pageNumber);
if (pageContainer) {
container.scrollTop = pageContainer.offsetTop;
} else {
console.warn('pdf pageContainer doesn\'t exist for index', pageNumber);
}
}
I used this site to copy examples and to ask help from various people so I thought I would share my attempt at putting things together with others who might be interested.
The following takes a range of pages from an existing PDF file and displays the result in an iframe or a new Tab. It uses [itextsharp][1]
The code contains a fair amount of novice code but at least it works.
There is absolutely no point point in asking me any questions because I almost certainly will not know the answer.
If anyone would like to point out where improvements might be made, I would be very grateful.
VIEW
<input id="btnIframe" type="button" value="Iframe" />
<input id="btnNewTab" type="button" value="New Tab" />
<div id="pdfDiv"></div>
<script type="text/javascript">
$(function () {
$("#btnIframe").click(function () {
var filename = "Test1";
var startPage = 1;
var endPage = 3;
var pParams = filename + "¬" + startPage + "¬" + endPage;
var url = '/PDFTest/GetPdfPages/' + pParams;
var html = "<iframe src=" + url + " style='width: 100%; height: 400px' ></iframe>";
$('#pdfDiv').html(html);
});
$("#btnNewTab").click(function () {
var filename = "Test1";
var startPage = 1;
var endPage = 3;
var pParams = filename + "¬" + startPage + "¬" + endPage;
var url = '/PDFTest/GetPdfPages/' + pParams;
window.open(url, "_blank");
});
});
</script>
CONTROLLER
public FileStreamResult GetPdfPages(string id)
{
var pParams = id.Split('¬');
var fileName = pParams[0];
var start = Convert.ToInt32(pParams[1]);
var end = Convert.ToInt32(pParams[2]);
var inputFile = Server.MapPath(#"~/PDFFiles/" + fileName + ".pdf");
var inputPdf = new PdfReader(inputFile);
int pageCount = inputPdf.NumberOfPages;
if (end < start || end > pageCount)
{
end = pageCount;
}
var inputDoc =
new Document(inputPdf.GetPageSizeWithRotation(1));
using (MemoryStream ms = new MemoryStream())
{
var outputWriter = PdfWriter.GetInstance(inputDoc, ms);
inputDoc.Open();
var cb1 = outputWriter.DirectContent;
for (var i = start; i <= end; i++)
{
inputDoc.SetPageSize(inputPdf.GetPageSizeWithRotation(i));
inputDoc.NewPage();
var page =
outputWriter.GetImportedPage(inputPdf, i);
int rotation = inputPdf.GetPageRotation(i);
if (rotation == 90 || rotation == 270)
{
cb1.AddTemplate(page, 0, -1f, 1f, 0, 0,
inputPdf.GetPageSizeWithRotation(i).Height);
}
else
{
cb1.AddTemplate(page, 1f, 0, 0, 1f, 0, 0);
}
}
inputDoc.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "inline;test.pdf");
Response.Buffer = true;
Response.Clear();
Response.OutputStream.Write(ms.GetBuffer(), 0, ms.GetBuffer().Length);
Response.OutputStream.Flush();
Response.End();
return new FileStreamResult(Response.OutputStream, "application/pdf");
}
}