Reputation: 33
Background:
I am try to fill in a pdf file that:
a) has form fields.
b) that is stored on Google Drive.
with data that is stored in Google spreadsheet.
I am using:
The work flow is:
The function is:
function showModalDialog_downloadFilledPDFform_NAMOFFORM() {
var pdf_template_file_url = getPDFfileURL("form1.pdf");
var htmlTemplate = HtmlService.createTemplateFromFile('downloadFilledPDFformHTML');
htmlTemplate.dataFromServerTemplate = { pdf_template_file: "form1.pdf", pdf_template_file_url: pdf_template_file_url };
var htmlOutput = htmlTemplate.evaluate();
htmlOutput.setWidth(648.1);
htmlOutput.setHeight(286.300)
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Download filled PDF');
}
The url that is passed to the htmlTemplate is generated by: "fillPDFForm.gs"
function: fillPDFForm.gs:
var pdfFileNamesAndIDs = [ ]
pdfFileNamesAndIDs.push(["form1.pdf", "1y8F5NgnK50mdtWSR6v1b8pELsbbBJert"])
pdfFileNamesAndIDs.push(["form2.pdf", "1B4BOaI-BqFmhmnFx7FaT-yys-U0vkYKz"])
pdfFileNamesAndIDs.push(["form3.pdf", "17LrJpRA5oBZBqw-2du1H74KxWIX55qYC"])
function getPDFfileURL(fileName) {
var documentID = "";
for (var i in pdfFileNamesAndIDs) {
//console.log(pdfFileNamesAndIDs[i][0]);
if (pdfFileNamesAndIDs[i][0] == fileName) {
documentID = pdfFileNamesAndIDs[i][1];
console.log("documentID: " + documentID);
}
}
var documentFile = DriveApp.getFileById(documentID);
var documentURL = documentFile.getDownloadUrl();
Logger.log("documentURL = "+documentURL);
return documentURL;
}
The Problem:
The URL generated by getPDFfileURL() doesn't work in the html file generated in showModalDialog_downloadFilledPDFform_NAMOFFORM().
The error in Chrome dev console is:
[email protected]:15 Uncaught (in promise) Error: Failed to parse PDF document (line:0 col:0 offset=0): No PDF header found
at e [as constructor] ([email protected]:15:189222)
at new e ([email protected]:15:190065)
at e.parseHeader ([email protected]:15:401731)
at e.<anonymous> ([email protected]:15:400782)
at [email protected]:15:1845
at Object.next ([email protected]:15:1950)
at [email protected]:15:887
at new Promise (<anonymous>)
at i ([email protected]:15:632)
at e.parseDocument ([email protected]:15:400580)
The basic concept for the html page (shown as a modal dialog box), came from: https://jsfiddle.net/Hopding/0mwfqkv6/3/
The contents of the htmlTemplate are:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<!-- Add Stylesheet -->
<?!= HtmlService.createHtmlOutputFromFile('downloadFilledPDFformCSS').getContent(); ?>
<!-- Add pdf-lib and downloadjs libraries -->
<!-- https://pdf-lib.js.org/ and https://github.com/rndme/download -->
<!-- https://jsfiddle.net/Hopding/0mwfqkv6/3/ -->
<script src="https://unpkg.com/[email protected]"></script>
<script src="https://unpkg.com/[email protected]"></script>
</head>
<body>
<h2 id="myTitle"></h2>
<p>Click the button to fill form fields in an the following PDF document: <code id="pdf_template_file">pdf-lib</code></p>
<button onclick="fillForm()">Fill PDF</button>
<p class="small">(Your browser will download the resulting file)</p>
</body>
<script>
const data = <?!= JSON.stringify(dataFromServerTemplate) ?>; //Stores the data directly in the javascript code
function removeExtension(filename) {
return filename.substring(0, filename.lastIndexOf('.')) || filename;
}
const pdf_template_file = data.pdf_template_file;
const pdf_template_file_basename = removeExtension(pdf_template_file);
// sample usage
function initialize() {
document.getElementById("myTitle").innerText = pdf_template_file;
//or use jquery: $("#myTitle").text(data.first + " - " + data.last);
document.getElementById("pdf_template_file").innerText = pdf_template_file;
}
// use onload or use jquery to call your initialization after the document loads
window.onload = initialize;
</script>
<script>
const { PDFDocument } = PDFLib;
async function fillForm() {
// Fetch the PDF with form fields
const formUrl = data.pdf_template_file_url
//const formPdfBytes = await fetch(formUrl).then(res => res.arrayBuffer())
const formPdfBytes = await fetch(formUrl, {
redirect: "follow",
mode: 'no-cors',
method: 'GET',
headers: {
'Content-Type': 'application/pdf',
}
}).then(res => res.arrayBuffer());
// Load a PDF with form fields
const pdfDoc = await PDFDocument.load(formPdfBytes);
// Get the form containing all the fields
const form = pdfDoc.getForm()
// Get all fields in the PDF by their names
const invIDField = form.getTextField('invID')
const makeAndModelField = form.getTextField('makeAndModel')
const nameField = form.getTextField('name')
const addressField = form.getTextField('address')
const phoneNumberField = form.getTextField('phoneNumber')
const emailAddressField = form.getTextField('emailAddress')
const dateField = form.getTextField('date')
// Output file name
const INPUT_FNAME = "AN"
const INPUT_LNAME = "Other"
// Fill in the basic info fields
invIDField.setText()
makeAndModelField.setText()
nameField.setText(INPUT_FNAME + " " + INPUT_LNAME)
addressField.setText()
phoneNumberField.setText()
emailAddressField.setText()
dateField.setText()
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc.save({updateFieldAppearances: false})
const outputPDFfilename = pdf_template_file_basename + "." + INPUT_FNAME + "_" + INPUT_LNAME + ".pdf"
// Trigger the browser to download the PDF document
download(pdfBytes, outputPDFfilename, "application/pdf");
}
</script>
</html>
I have replicated the contents of the html file on my testing webserver. The server has 3 files: index.html, stykesheet.css and form1.pdf
The pdf (on the web server) is the same pdf file that is stored on Google drive.
On my server the following works:
...but the following doesn't work:
So the question is:
How do I generate the correct URL (for the pdf file stored in Google Drive), so it can then be used by PDF-lib.js (in the htmlTemplate)?
Upvotes: 1
Views: 1787
Reputation: 33
The answer is:
This is the function that gets the pdf file and returns it as a base64 encoded string:
function getPDFfileAsBase64() {
var fileId = "";
var url = "https://drive.google.com/uc?id=" + fileId + "&alt=media";
console.log("url: " + url);
var params = {
method: "get",
headers: {
Authorization: "Bearer " + ScriptApp.getOAuthToken(),
},
};
var bytes = UrlFetchApp.fetch(url, params).getContent();
var encoded = Utilities.base64Encode(bytes);
//console.log("encoded: " + encoded);
return encoded;
}
the function to create a modal dialog is:
function showModalDialog_downloadFilledPDFform_form1() {
// Display a modal dialog box with custom HtmlService content.
var pdf_template_file = "form1.pdf";
var pdf_template_file_AsBase64 = getPDFfileAsBase64(pdf_template_file);
var htmlTemplate = HtmlService.createTemplateFromFile('downloadFilledPDFformHTML');
htmlTemplate.dataFromServerTemplate = { pdf_template_file: pdf_template_file, pdf_template_file_AsBase64: pdf_template_file_AsBase64};
var htmlOutput = htmlTemplate.evaluate();
htmlOutput.setWidth(648.1);
htmlOutput.setHeight(286.300)
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Download filled PDF');
}
Upvotes: 1