Is it possible to export (using Google Scripts) a single sheet to PDF without hiding sheets? - google-apps-script

I would like to be able to export a single specific sheet from a large workbook without having to hide the unrequired sheets. Is that actually possible with Google Scripts?
At the moment I am looping through a list of products, updating a query for each one and then exporting each result to an individual PDF. Basically creating a product "Printout" page for many products.
The code below works quite nicely but it starts by hiding all sheets other than my Printout page. That would be fine except some of the other sheets are protected and not all users that would be using my export functionality have the right to hide sheets.
I've considered adding an unprotect/protect function to my macro but it would be good to know if exporting a single sheet was an option before i went down this route?
The hiding sheets trick was from this post Export Single Sheet to PDF in Apps Script
function exportLoopedSheet(firstRow, lastRow) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetName = 'Printout'; // update for print sheet name
var productSheetName = 'ProductList'; // update for final product list
var folderName = 'productPDFs';
var main = ss.getSheetByName(sheetName);
var sheets = ss.getSheets();
var productList = ss.getSheetByName(productSheetName);
var lastProductRow = lastRow;
var firstProductRow = firstRow;
// Hide all sheets other than the Print Sheet
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() !== sheetName) {
sheets[i].hideSheet();
}
}
for (var prodNo = firstProductRow; prodNo < lastProductRow + 1; prodNo ++) {
var currentProduct = productList.getRange('A'+ prodNo).getValue();
main.getRange('B9').setValue(currentProduct);
// Ensure all changes are updated
SpreadsheetApp.flush();
// call the export sheet function
exportSheet();
}
// Unhide the sheets
for (i = 0; i < sheets.length; i++) {
sheets[i].showSheet();
}
}
function exportSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetName = 'Printout';
var main = ss.getSheetByName(sheetName);
var sheets = ss.getSheets();
//Hide All Empty Rows in the Print Sheet
var maxRows = main.getMaxRows();
var lastRow = main.getLastRow();
if (maxRows-lastRow != 0){
main.hideRows(lastRow+1, maxRows-lastRow);
}
// Save pdf version
var folder = 'productPDF';
var parentFolder = DriveApp.getFolderById('1234'); //add this line...
var folder, folders = DriveApp.getFoldersByName(folder);
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = parentFolder.createFolder(folder);
}
var name = main.getRange("B8").getValue();
folder.createFile(ss.getBlob().setName(name));
// Unhide the rows again
var fullSheetRange = main.getRange(1,1,main.getMaxRows(), main.getMaxColumns());
main.unhideRow(fullSheetRange);
}

Related

App Script Google Sheet creating multiple PDFs from 20+ sheets without it timing out

I have the below which hides all tabs but the current and exports to a pdf in a specified drive folder. Then unhides all, cycles to the next and repeats. Works fine but want to be able to scale the tab to fit to one page.
Other methods using URLfetchapp allow for scaling but it causes time outs by too many requests.
function Exportpdf() {
var spreadsheet = SpreadsheetApp.getActive();
nextSheet()
exportSheet()
if (SpreadsheetApp.getActiveSheet().getName()=='Data'){}
else {Exportpdf()}
};
function nextSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var numSheets = ss.getSheets().length; // number of sheets
var activeSheetIndex = ss.getActiveSheet().getIndex() - 1; // see below
var newSheetIndex = (activeSheetIndex + 1) % numSheets; // add 1; wrap around
ss.setActiveSheet(ss.getSheets()[newSheetIndex]);
}
function exportSheet() {
var actualSheetName = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() !== actualSheetName) {
sheets[i].hideSheet()
}
}
var pdfName = (SpreadsheetApp.getActiveSpreadsheet().getName() + " - " + SpreadsheetApp.getActiveSheet().getName());
var newFile = DriveApp.createFile(ss.getBlob().getAs('application/pdf').setName(pdfName));
for (var i = 0; i < sheets.length; i++) {
sheets[i].showSheet()
}
newFile.moveTo(DriveApp.getFolderById("11ZLhYZgpLuq5VWB90TdGbDrOUzP2SUlr"));
}

Copy Sheet to Google Doc

I am trying to copy a table from Google Sheet that includes pictures into a Google doc.
So far I managed to accomplish several things, but not the result that I need.
Source Google Sheet - table with pictures, filled by Importrange formula from another Google sheet.
Copying via appendTable does not copy pictures
I managed to export table to a pdf blob, but couldn't convert it to a regular Google doc then.
Any help appreciated.
function printDocument() {
var source = SpreadsheetApp.getActive();
var spreadsheetId=source.getId();
var sourcesheet = source.getSheetByName('Printable Shooting Plan');
var sheetId = sourcesheet.getSheetId();
var current_lastrow = getLastPopulatedRow(sourcesheet);
var srcData = sourcesheet.getRange('A1:G'+current_lastrow).getValues();
var parents = DriveApp.getFileById(source.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
var doc = DocumentApp.create("Shooting Plan");
var body = doc.getBody();
body.insertParagraph(0, 'Shooting Plan').setHeading(DocumentApp.ParagraphHeading.HEADING1).setAlignment(DocumentApp.HorizontalAlignment.CENTER);
table = body.appendTable(srcData);
table.getRow(0).editAsText().setBold(true);
// Copy doc to the directory we want it to be in. Delete it from root.
var docFile = DriveApp.getFileById(doc.getId());
folder.addFile(docFile);
DriveApp.getRootFolder().removeFile(docFile);
}
function onOpen() {
var submenu = [{name:"Save Doc", functionName:"printDocument"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu('Export', submenu);
}
function getLastPopulatedRow(sheet) {
var data = sheet.getDataRange().getValues();
for (var i = data.length-1; i > 0; i--) {
for (var j = 0; j < data[0].length; j++) {
if (data[i][j]) return i+1;
}
}
return 0;
}

CopyTo Method doesn't capture the Alt Text Titles from my Spreadsheet using Apps Script

I have a code that deletes sheet and copies sheet from another Spreadsheet to another. However, the source spreadsheet I have copied contains an images with Alt Text titles on it. When I tried to copy it to my destination file, the Alt Text titles are not captured.
Any thoughts on how to fix this? Thanks
function deleteAndReplaceSheet() {
var masterConfigSheetId = 'xxxxxxx';
var replicatedCardsFolderId = 'xxxxxxx';
var masterSheetName = 'Mastersheet'
var cardSheetName = "Card";
var ss = SpreadsheetApp.openById(masterConfigSheetId);
var masterSheetReference = ss.getSheetByName(masterSheetName);
var replicatedCards = DriveApp.getFolderById(replicatedCardsFolderId).getFiles();
var employeeNumbers = masterSheetReference.getRange("A2:A").getValues();
var employeeNumbersTrunc = [];
for(var i = 0; i < employeeNumbers.length; i++){
if(employeeNumbers[i][0] != "")
employeeNumbersTrunc.push("" + employeeNumbers[i][0]);
// Logger.log(employeeNumbersTrunc);
}
while (replicatedCards.hasNext()) {
var file = replicatedCards.next();
// Logger.log(file);
//check if employee number is existing inside the gdrive folder
if (employeeNumbersTrunc.indexOf(file.getName())==-1) {
continue;
}
try {
var activeSpreadSheet = SpreadsheetApp.open(file);
var destination = SpreadsheetApp.openById(file.getId());
var source = SpreadsheetApp.openById('xxxxxxx');
var sheet = source.getSheets()[0];
var toBeDeletedSheet = activeSpreadSheet.getSheetByName('Card');
//delete card sheetname
activeSpreadSheet.deleteSheet(toBeDeletedSheet);
sheet.copyTo(destination).setName('Card');
destination.moveActiveSheet(2);
} catch(err) {
}
}
}
Unfortunately, what you are trying to do is not possible. Since inserting images in cells is still a new feature in Google Sheets, the .copyTo() method doesn't support copying the Alt Text Titles.
I suggest you file a feature request at Google Issue Tracker at this link.

Preserve formula references when printing a single Google Sheets sheet to PDF

I want to accomplish the following:
Make a menu with a print to PDF button.
Have that button export a PDF to with the same name and same destination as the google sheet.
This works well, but the script I found I need to change, because most people print to PDF by making a temporary copy of the sheet, printing, and finally deleting the temporary copy.
I have references from other sheets in my original document that ends up printed as #REF! values because only the one sheet gets copied and printed, not my whole document.
How can i make this process include baking the formulas as text?
CODE FOR PRINTING:
function onOpen() {
var submenu = [{name: "Save PDF", functionName: "generatePdf"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu('Export', submenu);
}
function generatePdf() {
// Get active spreadsheet.
var sourceSpreadsheet = SpreadsheetApp.getActive();
// Get active sheet.
var sheets = sourceSpreadsheet.getSheets();
var sheetName = sourceSpreadsheet.getActiveSheet().getName();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
// Set the output filename as sheetName.
var pdfName = sheetName;
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(sourceSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
// Copy whole spreadsheet.
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]);
}
}
var destSheet = destSpreadsheet.getSheets()[0];
// Replace cell values with text (to avoid broken references).
var sourceRange = sourceSheet.getRange(1, 1, sourceSheet.getMaxRows(), sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
// 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);
}
CODE FOR CONVERTING FORMULAS TO TEXT:
function formulasAsText() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var k = 0; k < sheets.length; k++) {
var range = sheets[k].getDataRange();
var values = range.getValues();
var formulas = range.getFormulas();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[0].length; j++) {
values[i][j] = formulas[i][j] ? "'" + formulas[i][j] : values[i][j];
}
}
range.setValues(values);
}
}
To maintain formula references, you need to modify the formulasAsText() function to accept an input, and also not worry about writing a formula if one is found. This input could be a spreadsheet ID - i.e. the id of the temporary copy - or it could be an array of Sheet objects.
Once you have made these two modifications, you would call the function prior to deleting the non-desired sheets in the temporary copy:
/**
* #param Sheet[] wbSheets An array of Sheets to operate on.
* #param String toPreserve The name of the sheet which should be preserved.
*/
function preserveFormulas(wbSheets, toPreserve) {
if(!wbSheets || !wbSheets.length || !toPreserve)
throw new Error("Missing arguments.");
wbSheets.forEach(function (sheet) {
if ( sheet.getName() === toPreserve ) {
var range = sheet.getDataRange();
// Serialize the cell's current value, be it static or derived from a formula.
range.setValues(range.getValues());
}
});
}
Finished my script. Here is what i ended up with.
function onOpen() {
var submenu = [{name:"Save PDF", functionName:"generatePdf"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu('Export', submenu);
}
function hideSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("sheet1");
var sheet2 = ss.getSheetByName("PRINT SHEET");
var sheet3 = ss.getSheetByName("sheet3");
var sheet4 = ss.getSheetByName("sheet4");
var sheet5 = ss.getSheetByName("sheet5");
var sheet6 = ss.getSheetByName("sheet6");
var sheet7 = ss.getSheetByName("sheet7");
sheet1.hideSheet();
sheet3.hideSheet();
sheet4.hideSheet();
sheet5.hideSheet();
sheet6.hideSheet();
sheet7.hideSheet();
}
function showSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("sheet1");
var sheet2 = ss.getSheetByName("PRINT SHEET");
var sheet3 = ss.getSheetByName("sheet3");
var sheet4 = ss.getSheetByName("sheet4");
var sheet5 = ss.getSheetByName("sheet5");
var sheet6 = ss.getSheetByName("sheet6");
var sheet7 = ss.getSheetByName("sheet7");
sheet1.showSheet();
sheet3.showSheet();
sheet4.showSheet();
sheet5.showSheet();
sheet6.showSheet();
sheet7.showSheet();
}
function generatePdf() {
// Get active spreadsheet.
var sourceSpreadsheet = SpreadsheetApp.getActive();
// Get active sheet.
var sheets = sourceSpreadsheet.getSheets();
var sheetName = sourceSpreadsheet.getActiveSheet().getName();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
// Set the output filename as BID and number (from cell C4).
var pdfName = "BID " + sheets[0].getRange("C4").getValue() + ".pdf";
var ui = SpreadsheetApp.getUi();
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(sourceSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
hideSheets();
// Save to pdf.
var theBlob = sourceSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
showSheets();
ui.alert('Export finished');
}
Thanks.

Google App Script - getCommenter

I'm brand new to Google Apps Script, and I'm trying to create a simple spreadsheet that will allow me to share files by user email through a single spreadsheet.
I have written the following script, which will allow me to add editors and viewers, but not commenters.
I keep getting an error that states that the function addCommenter cannot be found in object spreadsheet.
function shareSheet () {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.toast('Updating access level...');
var sheet = ss.getSheets()[0];
var lastRow = sheet.getLastRow();
var range1 = sheet.getRange (3,1, lastRow,9);
var data = range1.getValues();
for (var i= 0; i < data.length; i++) {
var row = data [i];
var accessLevel = row [5];
var values = row [8];
var ss2 = SpreadsheetApp.openById(values);
var values2 = row [4];
// Add new editor
if (accessLevel == 'Edit') {
var addEditors = ss2.addEditor(values2);
}
// Add new viewer
if (accessLevel == 'View'){
var addViewers = ss2.addViewer(values2);
}
// Add new commenter
if (accessLevel == 'Comment') {
var addCommenters = ss2.addCommenter(values2);
}
}
}
The Spreadsheet object does not support the addCommentor() method. You should use DriveApp service instead.
DriveApp.getFileById(id).addCommenter(emailAddress)