Google apps script getAs('application/pdf') layout - pdf

I am generating a PDF file from a spreadsheet using an app script I found here.
This one uses the good old
Method getAs('application/pdf') and works great.
The problem is that the PDF document generated in this way has unwanted asymmetric margins (larger on the right, narrow on the left). I just wanted the page to be centered. The weird thing is that when I print from the Google menu File -> Print (or Ctrl + P) the document appears centered correctly.
My code looks like this:
function CreaPDF() {
//The function prints an invoice to PDF. First it copies spreadsheet to a new document.
//Deletes all sheet except the one to print. Saves it to PDF.
//It overwrites any existing doc with same name.
var sourceSpreadsheet = SpreadsheetApp.getActive();
var sheetName = "Factura";
var folderID = getParentFolder(); // Folder id to save in a folder.
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
var folder = DriveApp.getFolderById(folderID);
var numf = sourceSpreadsheet.getRangeByName("NumeroFactura").getValue();
var anof = numf.split("/",2); // Seeks number and year -> filename
var pdfName = anof[1] +"_Factura_" + anof[0]+ "_Dra_Salazar"; // Nombre del documento;
//Copy whole spreadsheet 2 temporary sheet
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder))
//delete redundant sheets
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
//Deletes pdf if already exists
var files = DriveApp.getFilesByName(pdfName);
while (files.hasNext()) {
files.next().setTrashed(true);
}
var destSheet = destSpreadsheet.getSheets()[0];
//repace cell values with text (to avoid broken references)
var sourceRange = sourceSheet.getRange(1, 1,sourceSheet.getMaxRows(),sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getDisplayValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
SpreadsheetApp.getActiveSpreadsheet().toast('Creando PDF');
//save to pdf
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
//Delete the temporary sheet
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
return true;
}
My settings when I print are:
Paper size: "A4"
Scale: "Normal" (I also tried: "fit to width" and
"fit on page")
Orientation: "Portrait"
Margins: "Normal"
As seen in the Google Help Forums it seems like an old issue with the way google prints. In summary, it seems that print settings are not saved and there is no way to pass any parameter to the getAs('application/pdf') method either. So i assume that the method (and menu print options) use default parameters that can not be modified. Any solution for this? The "Print or change page setup" help page does not help too much.
Thank you very much

Try this solution based on https://ctrlq.org/code/19869-email-google-spreadsheets-pdf
Using export url parameters you can set needed options for result pdf. Also you can set specific id of sheet to export, so you don't need to make duplicate of your whole spreadsheet anymore.
function CreaPDF() {
//The function prints an invoice to PDF. First it copies spreadsheet to a new document.
//Deletes all sheet except the one to print. Saves it to PDF.
//It overwrites any existing doc with same name.
var sourceSpreadsheet = SpreadsheetApp.getActive();
var sheetName = "Factura";
var folderID = getParentFolder(); // Folder id to save in a folder.
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
var folder = DriveApp.getFolderById(folderID);
var numf = sourceSpreadsheet.getRangeByName("NumeroFactura").getValue();
var anof = numf.split("/",2); // Seeks number and year -> filename
var pdfName = anof[1] +"_Factura_" + anof[0]+ "_Dra_Salazar"; // Nombre del documento;
SpreadsheetApp.getActiveSpreadsheet().toast('Creando PDF');
// export url
var url = 'https://docs.google.com/spreadsheets/d/'+sourceSpreadsheet.getId()+'/export?exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=A4' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=false' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='+sourceSheet.getSheetId(); // the sheet's Id
var token = ScriptApp.getOAuthToken();
// request export url
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
});
var theBlob = response.getBlob().setName(pdfName+'.pdf');
// delete pdf if already exists
var files = folder.getFilesByName(pdfName);
while (files.hasNext())
{
files.next().setTrashed(true);
}
// create pdf
var newFile = folder.createFile(theBlob);
return true;
}

Motivated by Kos' soultion.
I tracked request that created by google spreadsheet Download as GUI with chrome developer Tools. When you click next in Print settings, chrome sents post request and gets pdf file.
Let's cut to the chase. Below is my code.
var sourceSpreadsheet = SpreadsheetApp.getActive();
var url = 'https://docs.google.com/spreadsheets/d/' + sourceSpreadsheet.getId() + '/pdf?id=' + sourceSpreadsheet.getId()
var formData = {
'a': 'false',
'pc': '[null,null,null, ... SOME VERY LONG LIST OF NULLS AND NUMBERS ... 166]],[[0,5]]]],0]',
'gf': '[]'
};
var token = ScriptApp.getOAuthToken(); // doesn't needed when sheet is public
var options = {
'method': 'post',
'payload': formData,
'muteHttpExceptions': true,
headers: {
'Authorization': 'Bearer ' + token
}
};
var response = UrlFetchApp.fetch(url, options)
var theBlob = response.getBlob().setName('YourPDFName.pdf');
You can fill formData['pc'] by following:
1. Record requests at Network tab.
2. Click pdf?id=blahblah
pic for step 2
3. Scroll down to find Form Data
pic for step 3
4. Copy form data and fill your code.
Form data looks like:
[null,null,null,null,null,null,null,null,null,0,[["$Sheet_id"]],10000000,null,null,null,null,null,null,null,null,null,null,null,null,null,null,$day_after_30-12-1899,null,null,[$note,null,1,$note,0,0,0,0,$fixed_row,1,1,1,$headers,$footers,1,1],["A4",1,2,1,[0.75,0.75,0.7,0.7]],null,0,[["$Sheet_id",[[26,52]....],[[0,5]]]],0]
There are many more parameters but I can't find them all. Also parameters can be wrong as I found their value by checking changed value.
When you change $day_after_30-12-1899, date printed in pdf changes.
You can customize headers/footers. They are UTF16 encoded and has several control characters.
| Name in `Download as` GUI | character |
|---------------------------|-----------|
| page number A | \uee12 |
| page number B | \uee15 |
| page number C | \uee16 |
| Workbook title | \uee10 |
| sheet name | \uee11 |
| meaning in date & time | character |
|---------------------------|-----------|
| date formatstr start | \uee13 |
| year | yyyy |
| month | M |
| day | d |
| date formatstr end | \uee14 |
| | |
| time formatstr start | \uee17 |
| am or pm | am/pm |
| hour in 12hour clock | h |
| minute | mm |
you can use time and date like this YourText\uee13yy. M. d\uee14Yourtext
I'd recommend create formData['pc'] with GUI and modify $day_after_30-12-1899.
*Any corrections are welcome. While I'm not a native English speaker, there are many awkward/incorrect expressions.

Related

Google Docs PDF Export Adds Extra Blank Pages at the End

I use google spreadsheet to create invoices and send them by email in PDF format, it's been working great, my spreadsheets are yearly base, so every new year I create another one, all my spreadsheet from 2021 are still working fine, but all the new ones I've created for 2022 when I try to print or export PDFs, extra blank pages are beeing added to the end of the document. The number of blank pages are dependent on the number of blank lines on the sheet, but I didn't have this problem before, before even when the sheet has 1000 lines, if the invoice has only 1 page the PDF/Print will only generate 1 page, now if the sheet has 1000 lines it generates 19 blank pages.
Anyone with same problem or a solution?
Link for example: Spreadsheet link
Try to print or download the page and you will see the blank pages generated.
function savePDF() {
const ssh = SpreadsheetApp.getActiveSpreadsheet();
const invoice = ssh.getSheetByName('invoice');
const request = {
"method": "GET",
"headers":{"Authorization": "Bearer
"+ScriptApp.getOAuthToken()},
}
const key = ssh.getId();
const bogus = DriveApp.getRootFolder();
const fetch='https://docs.google.com/spreadsheets/d/'
+ key
+'/export?format=pdf&gid='
+ invoice.getSheetId()
+
'&size=letter&portrait=true
&printtitle=false
&pagenum=CENTER
&sheetnames=false
&gridlines=false
&top_margin=0.25
&bottom_margin=0.50&left_margin=0.25
&right_margin=0.25';
const name = "invoice.pdf";
let pdf = UrlFetchApp.fetch(fetch, request);
pdf = pdf.getBlob().setName(name);
const fold = "Folder ID goes here";
const folder = DriveApp.getFolderById(fold);
const file = folder.createFile(pdf);
}
The issue is that it prints the whole sheet by default. So it includes all those blank lines, thus adding blank sheets.
What you can do as a workaround is to CTRL + A in the sheet, then export using Selected cells
Export:
As you can see, total number of pages is only 1.
Script:
I'm not sure why it just doesn't work right now, but there might have been some changes and defaulted to export the whole sheet. To bypass this issue, you will need to add the actual range to the link. See modifications below:
Modifications:
Get the A1 notation of the data range:
const ssh = SpreadsheetApp.getActiveSpreadsheet();
const invoice = ssh.getSheetByName('invoice');
// add this line
const range = invoice.getDataRange().getA1Notation();
Append the range into the link:
const fetch = 'https://docs.google.com/spreadsheets/d/' + key
+ '/export?format=pdf&gid=' + invoice.getSheetId()
+ '&size=letter'
+ '&portrait=true'
+ '&printtitle=false'
+ '&pagenum=CENTER'
+ '&sheetnames=false'
+ '&gridlines=false'
+ '&top_margin=0.25'
+ '&bottom_margin=0.50'
+ '&left_margin=0.25'
+ '&right_margin=0.25'
// add range to link
+ '&range=' + range;
Output:

I need to Set Value the get URL in a cell

I am now need to convert the Google Sheet page to PDF, email to user and save the PDF format straightway to Google Drive.
And i need the Google Drive link after save it to Google Drive.
The steps from convert the Google Sheet to PDF, and i've done but I've stuck at getting the URL to be paste on the specific cells.
i know to get the URL using this code Logger.log(fileUrl)
But how to paste on cell the command ?
var changedFlag = false;
var TEMPLATESHEET='Boom-Report';
function emailSpreadsheetAsPDF() {
//Utilities.sleep(300000); //to pause for 60 seconds . Make sure photo completely upload to google sheet
DocumentApp.getActiveDocument();
DriveApp.getFiles();
// This is the link to my spreadsheet with the Form responses and the Invoice Template sheets
// Add the link to your spreadsheet here
// or you can just replace the text in the link between "d/" and "/edit"
// In my case is the text: 17I8-QDce0Nug7amrZeYTB3IYbGCGxvUj-XMt8uUUyvI
const ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1NVJOdFLBAgNFqSHhnHJYybjUlSqhv4hKI_HXJyhJ88E/edit");
// We are going to get the email address from the cell "B7" from the "Invoice" sheet
// Change the reference of the cell or the name of the sheet if it is different
const value = ss.getSheetByName("Source Email-Boom").getRange("X3").getValue();
const email = value.toString();
// Subject of the email message
const subject = ss.getSheetByName("Source Email-Boom").getRange("B3").getValue();
// Email Text. You can add HTML code here - see ctrlq.org/html-mail
const body = "Boom Lifts Inspection Report - Sent via Auto Generate PDI Report from Glideapps";
// Again, the URL to your spreadsheet but now with "/export" at the end
// Change it to the link of your spreadsheet, but leave the "/export"
const url = 'https://docs.google.com/spreadsheets/d/1NVJOdFLBAgNFqSHhnHJYybjUlSqhv4hKI_HXJyhJ88E/export?';
const exportOptions =
'exportFormat=pdf&format=pdf' + // export as pdf
'&size=A4' + // paper size letter / You can use A4 or legal
'&portrait=true' + // orientation portal, use false for landscape
'&fitw=true' + // fit to page width false, to get the actual size
'&sheetnames=false&printtitle=false' + // hide optional headers and footers
'&pagenumbers=false&gridlines=false' + // hide page numbers and gridlines
'&fzr=false' + // do not repeat row headers (frozen rows) on each page
'&gid=1832955909'; // the sheet's Id. Change it to your sheet ID.
// You can find the sheet ID in the link bar.
// Select the sheet that you want to print and check the link,
// the gid number of the sheet is on the end of your link.
var params = {method:"GET",headers:{"authorization":"Bearer "+ ScriptApp.getOAuthToken()}};
// Generate the PDF file
var response = UrlFetchApp.fetch(url+exportOptions, params).getBlob();
// Send the PDF file as an attachement
GmailApp.sendEmail("biha#equip-inc.com", subject, body, {
htmlBody: body,
attachments: [{
fileName: ss.getSheetByName("Source Email-Boom").getRange("B3").getValue().toString() +".pdf",
content: response.getBytes(),
mimeType: "application/pdf"
}]
});
// Save the PDF to Drive. (in the folder) The name of the PDF is going to be the name of the Company (cell B5)
const nameFile = ss.getSheetByName("Source Email-Boom").getRange("B3").getValue().toString() +".pdf"
const folderID = "1ZKWq9jWmeEQlxncuTPHssCFXC3Fidmxn";
DriveApp.getFolderById(folderID).createFile(response).setName(nameFile);
// create file URL
var SpreadsheetID = "1NVJOdFLBAgNFqSHhnHJYybjUlSqhv4hKI_HXJyhJ88E";
var ss2 = SpreadsheetApp.openById(SpreadsheetID);
var Sheetname2= "BL-Inspection Report";
var sheet2 = ss2.getSheetByName(Sheetname2);
// Get the last row based on the data range of a single column.
var lastRow2 = sheet2.getLastRow();
var lastColumn2 = sheet2.getLastColumn();
//EXAMPLE: Get the data range based on our selected columns range.
var dataRange2 = sheet2.getRange(1,1, lastRow2, lastColumn2);
var dataValues2 = dataRange2.getValues();
var dataMatch=[];
//***** */
// Loop through array and if condition met, add relevant
// background color.
var p=34 ; //Column No. for Name column AI:AI (Report No)
var filename = encodeURI(nameFile);
var files = DriveApp.getFilesByName(nameFile);
while (files.hasNext()) {
var file = files.next();
if (file) {
var fileUrl = file.getUrl();
};
};
////////////////HELP THIS PART////////////////////////////////
for ( j = 0 ; j < lastRow2 ; j++){
var zz=j;
var yy=dataValues2[j][34];
if(dataValues2[j][34] == subject){
var doclink = Logger.log(fileUrl);
var range = sheet2.getRange(j+1, 128);
range.setValue(doclink);
};
};
}
If cell B3 value in First Source is find in Google Drive, paste the URL in Column DX where the AI is same with First Source.
I believe your goal is as follows.
You want to search the file of filename subject retrieved from the cell "B3" of "Source Email-Boom" sheet from your Google Drive, and when the value of subject is found from the column "AI" of "BL-Inspection Report" sheet, you want to put the URL of the file to the column "AJ".
For my question of For example, you want to put the URL of the just created file?, from Yes of your replying, I understood that you wanted to put the URL of the just created file in this script.
In this case, how about the following modification? I thought that in this case, the file URL of the just created file can be directly retrieved from DriveApp.getFolderById(folderID).createFile(response).setName(nameFile). So, how about the following modification?
From:
DriveApp.getFolderById(folderID).createFile(response).setName(nameFile);
To:
var fileUrl = DriveApp.getFolderById(folderID).createFile(response).setName(nameFile).getUrl();
And also, please modify as follows.
From:
var filename = encodeURI(nameFile);
var files = DriveApp.getFilesByName(nameFile);
while (files.hasNext()) {
var file = files.next();
if (file) {
var fileUrl = file.getUrl();
};
};
////////////////HELP THIS PART////////////////////////////////
for (j = 0; j < lastRow2; j++) {
if (dataValues2[j][34] == subject) {
var doclink = Logger.log(fileUrl);
var range = sheet2.getRange(j + 1, 128);
range.setValue(doclink);
};
};
To:
var range = sheet2.getRange("AI2:AI" + sheet2.getLastRow()).createTextFinder(subject).findNext();
if (range) {
range.offset(0, 1).setValue(fileUrl);
}
In this modification, the cell is searched using TextFinder.
Reference:
createTextFinder(findText)

Convert PDF from Google Drive to Image and send to Telegram via Bot

Currently the step by step I use is as follows:
First step -> Create a PDF of the Page Jogos na TV from my spreadsheet:
function CreatePDF() {
var ss = SpreadsheetApp.getActive();
SpreadsheetApp.flush();
var theurl = 'https://docs.google.com/a/mydomain.org/spreadsheets/d/' +
'ID CODE TO SPREADSHEET' +
'/export?format=pdf' +
'&size=0' +
'&portrait=true' +
'&fitw=true' +
'&top_margin=0' +
'&bottom_margin=0' +
'&left_margin=0' +
'&right_margin=0' +
'&sheetnames=false&printtitle=false' +
'&pagenum=false' +
'&gridlines=false' +
'&fzr=FALSE' +
'&gid=' +
'ID CODE TO SPREADSHEET PAGE';
var token = ScriptApp.getOAuthToken();
var docurl = UrlFetchApp.fetch(theurl, { headers: { 'Authorization': 'Bearer ' + token } });
var pdfBlob = docurl.getBlob();
//...get token and Blob (do not create the file);
var fileName = ss.getSheetByName("Jogos na TV").getRange("A1").getValue();
//Access or create the 'PDF' folder;
var folder;
var folders = DriveApp.getFoldersByName("PDF");
if(folders.hasNext()) {
folder = folders.next();
}else {
folder = DriveApp.createFolder("PDF");
}
//Remove duplicate file with the same name;
var existing = folder.getFilesByName(fileName);
if(existing.hasNext()) {
var duplicate = existing.next();
if (duplicate.getOwner().getEmail() == Session.getActiveUser().getEmail()) {
var durl = 'https://www.googleapis.com/drive/v3/files/'+duplicate.getId();
var dres = UrlFetchApp.fetch(durl,{
method: 'delete',
muteHttpExceptions: true,
headers: {'Authorization': 'Bearer '+token}
});
var status = dres.getResponseCode();
if (status >=400) {
} else if (status == 204) {
folder.createFile(pdfBlob.setName(fileName));
}
}
} else {
folder.createFile(pdfBlob.setName(fileName));
}
}
Second Step -> Manually copy the PDF link created in Google Drive
Step Three -> I send the text with the PDF minature to my group on Telegram:
function EnviarTelegram(botSecret, chatId, photoUrl, caption) {
var response = UrlFetchApp.fetch("https://api.telegram.org/bot" + botSecret + "/sendPhoto?caption=" + encodeURIComponent(caption) + "&photo=" + encodeURIComponent(photoUrl) + "&chat_id=" + chatId + "&parse_mode=HTML");
}
The current formula for sending to Telegram via spreadsheet:
=EnviarTelegram("botSecret","chatId","Url to PDF","Programação de jogos na TV
"&TEXT('Jogos Hoje'!B1,"DD/MM/YYYY")&" e "&TEXT('Jogos Amanhã'!B1,"DD/MM/YYYY"))
The thumbnail created for the PDF is cut so the image sent to Telegram is also cut and the spreadsheets cannot be saved as an image, just document or PDF.
Is there any way to automatically convert PDF to image and be able to send to Telegram?
Issue and workarounds:
Unfortunately, there are no methods for directly converting the PDF format to the image data in the methods of Google Apps Script. So, in this case, I thought that it is required to use the workarounds for achieving your goal.
Workaround 1:
In this workaround, the external API is used. When you want to directly convert the PDF data to an image data, how about using an external API? Ref
You can see the sample script for this at this thread.
Workaround 2:
In this workaround, the range of sheet is exported as an image. When I saw your Spreadsheet, it seems that the data range of a sheet in Google Spreadsheet is exported as a PDF data. From this situation, as the other workaround, how about converting the range of Spreadsheet to an image?
You can see the sample script for this at this thread.
Workaround 3:
In this workaround, the PDF data is sent with sendDocument. Ref In this case, it seems that the data is required to be sent as multipart/form-data. The sample script is as follows.
Sample script:
var url = "https://api.telegram.org/bot" + botSecret + "/sendDocument?chat_id=" + chatId;
var blob = DriveApp.getFileById("### file ID of PDF file ###").getBlob();
var res = UrlFetchApp.fetch(url, {method: "post", payload: {document: blob}});
console.log(res.getContentText())
References:
PDF to PNG API
sendDocument
Related threads.
Convert a gdoc into image
How to copy a range from a spreadsheet as an image to Google Slides?

File size increases when converting a Google Sheet into PDF format in Apps script

I have created a script where in Google Sheet is converted to PDF and sent in an email (via Gmail API). for certain records, the PDF size gets increased up to 10 MB (with script) and actually the file size is 130 KB when downloaded manually.
Please find the below syntax which is used to convert sheet to PDF
var blob=DriveApp.getFileById(<<Google Sheet ID>>).getAs('application/pdf');
What could be the possible reason for this issue and How to resolve it?
Your spreadsheet might have many empty cells which get converted into the pdf the way you do it.
I recommend you instead to convert in a more manual way, with the UrlfetchApp - this allows you to specify different options, among others the range you want to convert.
The following sample shows how to export only the data with contents (dataRange) for a spreadsheet that contains onlyone sheet:
function myFunction() {
var token = ScriptApp.getOAuthToken();
var ss = SpreadsheetApp.openById("<<Google Sheet ID>>");
var sheet = ss.getActiveSheet();
var name = ss.getName();
var url = "https://docs.google.com/spreadsheets/d/" + "<<Google Sheet ID>>" + "/export?";
var options = 'exportFormat=pdf&format=pdf' // export format
+ '&size=A4' // paper size
+ '&portrait=true' // orientation
var range = sheet.getDataRange().getA1Notation();
var response = UrlFetchApp.fetch(url + options + '&gid=' + sheet.getSheetId() + "&range=" + range,
{
headers: {
'Authorization': 'Bearer ' + token
},
muteHttpExceptions:true
});
var blob = DriveApp.createFile(response.getBlob().setName(name));
}

Convert all sheets to PDF with Google Apps Script

I'm trying to convert a Google spreadsheet with multiple sheets to a PDF file. The script below works, but it only creates a PDF with the last page of the spreadsheet.
function savePDFs() {
SpreadsheetApp.flush();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var url = ss.getUrl();
//remove the trailing 'edit' from the url
url = url.replace(/edit$/,'');
//additional parameters for exporting the sheet as a pdf
var url_ext = 'export?exportFormat=pdf&format=pdf' + //export as pdf
//below parameters are optional...
'&size=letter' + //paper size
'&portrait=false' + //orientation, false for landscape
'&fitw=true' + //fit to width, false for actual size
'&sheetnames=false&printtitle=false&pagenumbers=false' + //hide optional
'&gridlines=false' + //false = hide gridlines
'&fzr=false' + //do not repeat row headers (frozen rows) on each page
'&gid='; //leave ID empty for now, this will be populated in the FOR loop
var token = ScriptApp.getOAuthToken();
//make an empty array to hold your fetched blobs
var blobs = [];
//.fetch is called for each sheet, the response is stored in var blobs[]
for(var i = 0; i < sheets.length; i++) {
var sheetname = sheets[i].getName();
//if the sheet is one that you don't want to process,
//continue' tells the for loop to skip this iteration of the loop
if(sheetname == "Team Member Numbers")
continue;
//grab the blob for the sheet
var response = UrlFetchApp.fetch(url + url_ext + sheets[i].getSheetId(), {
headers: {
'Authorization': 'Bearer ' + token
}
});
//convert the response to a blob and store in our array
blobs.push(response.getBlob().setName(sheets[i].getName() + '.pdf'));
var array_blob = response.getBlob().setName(sheets[i].getName() + '.pdf');
}
//from here you should be able to use and manipulate the blob to send and
//email or create a file per usual.
// send email
var subject = "Enter Subject"
var message = "See attached PDF"
MailApp.sendEmail("email addy here", subject, message,{attachments:[array_blob]});
}
I've tweaked #Mogsdad code slightly to print the entire spreadsheet as one PDF. The key is tweaking the export parameter. Basically replace
'&gid=' + sheet.getSheetId() //the sheet's Id
with
(optSheetId ? ('&gid=' + sheet.getSheetId()) : ('&id=' + ss.getId())) // Print either the entire Spreadsheet or the specified sheet if optSheetId is provided
So the code above minus the looping looks like:
function savePDFs( optSSId, optSheetId ) {
// If a sheet ID was provided, open that sheet, otherwise assume script is
// sheet-bound, and open the active spreadsheet.
var ss = (optSSId) ? SpreadsheetApp.openById(optSSId) : SpreadsheetApp.getActiveSpreadsheet();
// Get folder containing spreadsheet, for later export
var parents = DriveApp.getFileById(ss.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
//additional parameters for exporting the sheet as a pdf
var url_ext = 'export?exportFormat=pdf&format=pdf' //export as pdf
// Print either the entire Spreadsheet or the specified sheet if optSheetId is provided
+ (optSheetId ? ('&gid=' + sheet.getSheetId()) : ('&id=' + ss.getId()))
// following parameters are optional...
+ '&size=letter' // paper size
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=true' // fit to width, false for actual size
+ '&sheetnames=false&printtitle=false&pagenumbers=false' //hide optional headers and footers
+ '&gridlines=false' // hide gridlines
+ '&fzr=false'; // do not repeat row headers (frozen rows) on each page
var options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
}
}
var response = UrlFetchApp.fetch("https://docs.google.com/spreadsheets/" + url_ext, options);
var blob = response.getBlob().setName(ss.getName() + '.pdf');
//from here you should be able to use and manipulate the blob to send and email or create a file per usual.
//In this example, I save the pdf to drive
folder.createFile(blob);
}
Btw, thank you -- I've been looking for a solution for this for a long time!
This function is an adaptation of a script provided by "ianshedd..." here.
It:
Generates PDFs of ALL sheets in a spreadsheet, and stores them in the same folder containing the spreadsheet. (It assumes there's just one folder doing that, although Drive does allow multiple containment.)
Names pdf files with Spreadsheet & Sheet names.
Uses the Drive service (DocsList is deprecated.)
Can use an optional Spreadsheet ID to operate on any sheet. By default, it expects to work on the "active spreadsheet" containing the script.
Needs only "normal" authorization to operate; no need to activate advanced services (well... you do need some, see this) or fiddle with oAuthConfig.
OAuth2 Authorization for the fetch() call that retrieves the PDF of a spreadsheet is granted via ScriptApp.getOAuthToken(), which gives us the OAuth 2.0 access token for the current user.
With a bit of research and effort, you could hook up to an online PDF Merge API, to generate a single PDF file. Barring that, and until Google provides a way to export all sheets in one PDF, you're stuck with separate files. See Gilbert's tweak for a way to get multiple sheets!
Script:
/**
* Export one or all sheets in a spreadsheet as PDF files on user's Google Drive,
* in same folder that contained original spreadsheet.
*
* Adapted from https://code.google.com/p/google-apps-script-issues/issues/detail?id=3579#c25
*
* #param {String} optSSId (optional) ID of spreadsheet to export.
* If not provided, script assumes it is
* sheet-bound and opens the active spreadsheet.
* #param {String} optSheetId (optional) ID of single sheet to export.
* If not provided, all sheets will export.
*/
function savePDFs( optSSId, optSheetId ) {
// If a sheet ID was provided, open that sheet, otherwise assume script is
// sheet-bound, and open the active spreadsheet.
var ss = (optSSId) ? SpreadsheetApp.openById(optSSId) : SpreadsheetApp.getActiveSpreadsheet();
// Get URL of spreadsheet, and remove the trailing 'edit'
var url = ss.getUrl().replace(/edit$/,'');
// Get folder containing spreadsheet, for later export
var parents = DriveApp.getFileById(ss.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
// Get array of all sheets in spreadsheet
var sheets = ss.getSheets();
// Loop through all sheets, generating PDF files.
for (var i=0; i<sheets.length; i++) {
var sheet = sheets[i];
// If provided a optSheetId, only save it.
if (optSheetId && optSheetId !== sheet.getSheetId()) continue;
//additional parameters for exporting the sheet as a pdf
var url_ext = 'export?exportFormat=pdf&format=pdf' //export as pdf
+ '&gid=' + sheet.getSheetId() //the sheet's Id
// following parameters are optional...
+ '&size=letter' // paper size
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=true' // fit to width, false for actual size
+ '&sheetnames=false&printtitle=false&pagenumbers=false' //hide optional headers and footers
+ '&gridlines=false' // hide gridlines
+ '&fzr=false'; // do not repeat row headers (frozen rows) on each page
var options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
}
}
var response = UrlFetchApp.fetch(url + url_ext, options);
var blob = response.getBlob().setName(ss.getName() + ' - ' + sheet.getName() + '.pdf');
//from here you should be able to use and manipulate the blob to send and email or create a file per usual.
//In this example, I save the pdf to drive
folder.createFile(blob);
}
}
/**
* Dummy function for API authorization only.
* From: https://stackoverflow.com/a/37172203/1677912
*/
function forAuth_() {
DriveApp.getFileById("Just for authorization"); // https://code.google.com/p/google-apps-script-issues/issues/detail?id=3579#c36
}
I do not yet have the reputation to comment, but there seems to be a minor issue with the top answer above as submitted by Gilbert W... though it's just as likely that I failed to understand something.
That solution includes the line
+ (optSheetId ? ('&gid=' + sheet.getSheetId()) : ('&id=' + ss.getId()))
However, "sheet" has not been defined in the code prior to this point. In Mogsdad's code, "sheet" is defined within the loop that was removed:
for (var i=0; i<sheets.length; i++) {
var sheet = sheets[i];
And "sheets" was defined as
var sheets = ss.getSheets();
The solution works for someone who wants to print the entire spreadsheet, which is the question that was asked. However, the code no longer works for someone who wants to print a single page.
Another issue with Gilbert's updated code was that the HTML request included a reference to the sheet ID, but not the spreadsheet itself. This caused the response to fail if you provided a specific sheet ID, though it works fine if no sheet ID was provided. I got it to work again by reverting the URL base to the way Mogsdad had it.
Another tweak: Gilbert's code automatically names the new .PDF as whatever the spreadsheet was named. Meanwhile, Mogsdad's code prints out every sheet one at a time, naming each .PDF with the spreadsheet name followed by the name of the current sheet. I wanted to print the PDF with the name of the single sheet, if applicable, and also provide the user the ability to specify an output name.
Since no method exists to "getSheetById", depending on the context of your code, it probably makes more sense for the function to take "optSheetName" instead of "optSheetID." The sheet ID can be grabbed from "getSheetByName" if needed, and it seems to me a user is generally more likely to have the sheet's name than the sheet's ID. Both the name and the ID can be obtained programmatically from a bound script, but only the name can be used to get a specific existing sheet.
I also added an optional email parameter so that you can print and email the PDF at the same time.
Here is my version:
function savePDFs( optSSId , optSheetName , optOutputName, optEmail) {
// If a sheet ID was provided, open that sheet, otherwise assume script is
// sheet-bound, and open the active spreadsheet.
var ss = (optSSId) ? SpreadsheetApp.openById(optSSId) : SpreadsheetApp.getActiveSpreadsheet();
var optSheetId = ss.getSheetByName(optSheetName).getSheetId();
var outputName = (optOutputName ? optOutputName : (optSheetName ? optSheetName : ss.getName()))
// Get folder containing spreadsheet, for later export
var parents = DriveApp.getFileById(ss.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
var url_base = ss.getUrl().replace(/edit$/,'');
//additional parameters for exporting the sheet as a pdf
var url_ext = 'export?exportFormat=pdf&format=pdf' //export as pdf
// Print either the entire Spreadsheet or the specified sheet if optSheetId is provided
+ (optSheetId ? ('&gid=' + optSheetId) : ('&id=' + ss.getId())) // Print either the entire Spreadsheet or the specified sheet if optSheetId is provided
// following parameters are optional...
+ '&size=letter' // paper size
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=true' // fit to width, false for actual size
+ '&sheetnames=false&printtitle=false&pagenumbers=false' //hide optional headers and footers
+ '&gridlines=false' // hide gridlines
+ '&fzr=false'; // do not repeat row headers (frozen rows) on each page
var options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken(),
}
}
var response = UrlFetchApp.fetch(url_base + url_ext, options);
var blob = response.getBlob().setName((outputName)+ '.pdf');
folder.createFile(blob);
GmailApp.sendEmail(optEmail, "Here is a file named " + outputName, "Please let me know if you have any questions or comments.", {attachments:blob});
}
Here's my variation on this theme, based on Dr Queso's answer.
All parameters (described in the code) are optional and if none are specified it uses the active spreadsheet, converts all of the tabs into a single PDF named after the spreadsheet and doesn't email the PDF.
function test() {
// Create a PDF containing all the tabs in the active spreadsheet, name it
// after the spreadsheet, and email it
convertSpreadsheetToPdf('user#email.com')
// Create a PDF containing all the tabs in the spreadsheet specified, name it
// after the spreadsheet, and email it
convertSpreadsheetToPdf('user#email.com', '1r9INcnsyvSQmeduJWVYAvznOOYei9jeAjsy0acA3G1k')
// Create a PDF just containing the tab 'Sheet2' in the active spreadsheet, specify a name, and email it
convertSpreadsheetToPdf('user#email.com', null, 'Sheet2', 'PDF 3')
}
/*
* Save spreadsheet as a PDF
*
* #param {String} email Where to send the PDF [OPTIONAL]
* #param {String} spreadsheetId Or the active spreadsheet[OPTIONAL]
* #param {String} sheetName The tab to output [OPTIONAL]
* #param {String} PdfName [OPTIONAL]
*/
function convertSpreadsheetToPdf(email, spreadsheetId, sheetName, pdfName) {
var spreadsheet = spreadsheetId ? SpreadsheetApp.openById(spreadsheetId) : SpreadsheetApp.getActiveSpreadsheet();
spreadsheetId = spreadsheetId ? spreadsheetId : spreadsheet.getId()
var sheetId = sheetName ? spreadsheet.getSheetByName(sheetName).getSheetId() : null;
var pdfName = pdfName ? pdfName : spreadsheet.getName();
var parents = DriveApp.getFileById(spreadsheetId).getParents();
var folder = parents.hasNext() ? parents.next() : DriveApp.getRootFolder();
var url_base = spreadsheet.getUrl().replace(/edit$/,'');
var url_ext = 'export?exportFormat=pdf&format=pdf' //export as pdf
// Print either the entire Spreadsheet or the specified sheet if optSheetId is provided
+ (sheetId ? ('&gid=' + sheetId) : ('&id=' + spreadsheetId))
// following parameters are optional...
+ '&size=letter' // paper size
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=true' // fit to width, false for actual size
+ '&sheetnames=false&printtitle=false&pagenumbers=false' //hide optional headers and footers
+ '&gridlines=false' // hide gridlines
+ '&fzr=false'; // do not repeat row headers (frozen rows) on each page
var options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken(),
}
}
var response = UrlFetchApp.fetch(url_base + url_ext, options);
var blob = response.getBlob().setName(pdfName + '.pdf');
folder.createFile(blob);
if (email) {
var mailOptions = {
attachments:blob
}
MailApp.sendEmail(
email,
"Here is a file named " + pdfName,
"Please let me know if you have any questions or comments.",
mailOptions);
}
} // convertSpreadsheetToPdf()
NOTE: This doesn't work if the sheet is hidden. Use activate() to unhide a sheet.