Reputation: 2686
I have a simple structure that generates a PDF from jsPDF, gets the 'datauristring' and passes that into an AJAX request. However, the AJAX method doesn't fire. When it tries to 'send' it throws an exception 'h is undefined'. I figured that out from tracing the XHR requests and that appears in the jQuery lib. Can I not fire the AJAX request so soon after the callback from jsPDF?
I should note i'm sending the PDF to a service that is combining that pdf with another and returning a brand new document.
var docX = new jsPDF("p", "pt", "letter");
margins = {
top: 20,
bottom: 20,
left: 20,
width: 522
};
specialElementHandlers = {
// element with id of "bypass" - jQuery style selector
'#bypassme': function (element, renderer) {
// true = "handled elsewhere, bypass text extraction"
return true
}
};
docX.fromHTML (
document.getElementById('productModalContent'),
margins.left,
margins.top, {
'width' : margins.width,
'elementHandlers': specialElementHandlers
},
function (dispose) {
specDoc = (docX.output('datauristring'));
console.log(dispose);
//docX.save('asdfas.pdf');
//console.log(specDoc);
processPDF(specDoc);
}, margins
);
function processPDF(doc) {
console.log(doc.length);
$.ajax({
type: "POST",
url: "https://someurl",
data: {
'partNumber' : '$!{rawPartNumber}',
'region' : '$!{regionContentlet.code}',
'doc' : doc
},
cache: false,
success: function(data) {
console.log("success?");
var link=document.createElement('a');
link.href='data:application/pdf;base64,' + data;
link.download="myfile.pdf";
link.setAttribute('target', '_blank');
link.click();
},
error: function(data) {
console.log(data);
}
});
}
EDIT: remove ajax and added fetch.
const response = await fetch('myurl', {
method: 'POST',
body: {
'partNumber' : '$!{rawPartNumber}',
'region' : '$!{regionContentlet.code}',
'myDiv' : doc
}
});
Upvotes: 0
Views: 925
Reputation: 37885
the reason why you can't use the first approch is b/c you have to fetch it as a blob, so that jQuery nor xhr dose not try to encode binary data into text. That way you will get corrupt data.
You need to get it as raw data. jQuery is bad at this b/c you can't change the responsetype
to be a blob. so you need to write the xhr request yourself or use the fetch api fetch(url).then(r => r.blob()).then(code)
but if i where you i whould have reverted back from base64 to raw binary to save data and used a (perhaps hidden) <from method="https://someurl" hidden>
to download the pdf using correct response headers
Upvotes: 0
Reputation: 2686
For anyone that might stop by this question, I did solve my issue. I wasn't able to get the AJAX call to work from within jsPDF's fromHTML function. I ended up getting the first PDF as a base64 string, then calling the jsPDF's fromHTML for the second PDF set of data and finally using pdf-lib to stitch the two PDFs together all in js.
<script src="https://unpkg.com/jspdf@latest/dist/jspdf.min.js"></script>
<script src="https://unpkg.com/pdf-lib/dist/pdf-lib.js"></script>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>
<script>
async function processPDF() {
$.ajax({
type: "POST",
url: 'mypdfurl',
data: {
},
cache: false,
success: function(data) {
console.log("success?");
var docX = new jsPDF("p", "pt", "letter");
margins = {
top: 20,
bottom: 20,
left: 20,
width: 575,
};
specialElementHandlers = {
// element with id of "bypass" - jQuery style selector
'#bypassme': function (element, renderer) {
// true = "handled elsewhere, bypass text extraction"
return true
},
'.info': function (element, renderer) {
element.style.fontSize = "12px";
},
'.spec': function (element, renderer) {
element.style.fontSize = "12px";
},
'.attribute': function (element, renderer) {
element.style.fontSize = "12px";
},
'#logoProductPrint': function(element, renderer) {
var ele = element.firstElementChild;
ele.setAttribute("style", "width:80px;height:50px");
},
};
var itm = document.getElementById('productPrint');
var itm_p = itm.cloneNode(true);
synchronizeCssStyles(itm, itm_p, true);
docX.fromHTML (
//document.getElementById('productPrint'),
itm_p,
margins.left,
margins.top,
{
'width' : margins.width,
'elementHandlers': specialElementHandlers,
},
async function (dispose) {
var specDoc = (docX.output('datauristring'));
const pdfDoc = await PDFLib.PDFDocument.create();
var d = 'data:application/pdf;base64,' + data;
const firstDoc = await PDFLib.PDFDocument.load(d);
const secondDoc = await PDFLib.PDFDocument.load(specDoc);
const firstPage = await pdfDoc.copyPages(firstDoc, [0]);
pdfDoc.addPage(firstPage[0]);
const secondPage = await pdfDoc.copyPages(secondDoc, [0]);
pdfDoc.addPage(secondPage[0]);
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const blobUrl = URL.createObjectURL(blob);
var link=document.createElement('a');
link.href=blobUrl;
link.setAttribute('target', '_blank');
link.click();
}, margins
);
},
error: function(data) {
console.log(data);
}
});
function synchronizeCssStyles(src, destination, recursively) {
// if recursively = true, then we assume the src dom structure and destination dom structure are identical (ie: cloneNode was used)
// window.getComputedStyle vs document.defaultView.getComputedStyle
// @TBD: also check for compatibility on IE/Edge
destination.style.cssText = document.defaultView.getComputedStyle(src, "").cssText;
if (recursively) {
var vSrcElements = src.getElementsByTagName("*");
var vDstElements = destination.getElementsByTagName("*");
for (var i = vSrcElements.length; i--;) {
var vSrcElement = vSrcElements[i];
var vDstElement = vDstElements[i];
// console.log(i + " >> " + vSrcElement + " :: " + vDstElement);
vDstElement.style.cssText = document.defaultView.getComputedStyle(vSrcElement, "").cssText;
}
}
}
}
Upvotes: 1