Saving as PDF with images and in landscape [duplicate] - google-apps-script

This question already has an answer here:
Creating PDF in Landscape (Google Apps Script)
(1 answer)
Closed 3 months ago.
I have a code that, upon a submission from a linked form, will select the row with the most recently submitted information, which auto-fills another sheet in the format I prefer and then saves that as a PDF. I have used this code for several worksheets but now I have need of it on a sheet that contains an image. My PDF is saving without the image, which is essential to the process. In addition to this, I would also like it to save in landscape if someone could help with that, I'd appreciate it.
I've played around with the code a bit, but I had help with writing it and don't understand the language enough to make this work.
function generatePdf() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheeta = ss.getSheetByName('Firstsheet');
sheeta.getRange("A2:A").clear();
var lastrow = sheeta.getLastRow();
var range = sheeta.getRange(lastrow, 1);
var values = range.setValue("autofill"); //This is a checkbox in column A which triggers the vlookup on the second sheet
var originalSpreadsheet = SpreadsheetApp.getActive();
var sourcesheet = originalSpreadsheet.getSheetByName("Secondsheet");
var sourcerange = sourcesheet.getRange('A:I');
var sourcevalues = sourcerange.getValues();
var data = sourcesheet.getDataRange().getValues();
var pdfname = sourcesheet.getRange('E34').getDisplayValue();
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export");
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var projectname = SpreadsheetApp.getActiveSpreadsheet();
var sheet = sourcesheet.copyTo(newSpreadsheet);
var destrange = sheet.getRange('A:I');
destrange.setValues(sourcevalues);
newSpreadsheet.getSheetByName('Sheet1').activate();
newSpreadsheet.deleteActiveSheet();
var pdf = DriveApp.getFileById(newSpreadsheet.getId());
var theBlob = pdf.getBlob().getAs('application/pdf').setName("Sheet" + pdfname);
var folderID = "folder ID goes here";
var folder = DriveApp.getFolderById(folderID);
var newFile = folder.createFile(theBlob);
DriveApp.getFileById(newSpreadsheet.getId()).setTrashed(true);
sheeta.getRange("A2:A").clear();
}
I need the image in A1:F29 (merged) to save into the intermediary sheet that this formula creates to then save to the PDF. It would also be nice to save in landscape if at all possible.

1) The problem with the missing image is that you’re making the copy process twice, the first one using copyTo() function which copy the all sheet correctly. And then a second one where you use:
var destrange = sheet.getRange('A:I');
destrange.setValues(sourcevalues);
Which copy all the data, even “over the cell” images but not “in cell” images (probably because this is a new Sheets feature), which probably is the problem you’re facing. So you should delete those 2 lines of code in order to not override the first copy process. That’s what i did and worked fine.
2) As there is no option to specify the landscape feature, you can use the method they used in the link provided by #cooper [1] making a request to the export Url. I implemented the code and worked as intended, you just have to erase these 2 lines:
var pdf = DriveApp.getFileById(newSpreadsheet.getId());
var theBlob = pdf.getBlob().getAs('application/pdf').setName("Sheet" + pdfname);
For this:
var url = newSpreadsheet.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...
'&portrait=false' + //orientation, false for landscape
'&gid=' + newSpreadsheet.getSheetId(); //the sheet's Id
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url + url_ext, {
headers: {
'Authorization': 'Bearer ' + token
}
});
var theBlob = response.getBlob().setName("Sheet" + pdfname);
[1] https://ctrlq.org/code/19869-email-google-spreadsheets-pdf

Related

Google Scripts Editor-Overwrite backup file

I am working with Google Scripts Editor. The purpose of my script is to backup a spreadsheet from Google Sheets to my local as a csv. I would like each new backup to overwrite the previous backup file. Currently the script results in subsequent backup files as the same name but as a copy (Ex. filename (1).csv and filename (2).csv) Any help in overwriting would be appreciated.
function myFunction() {// UPDATE THE FOLDER ID for e.g. "My Drive > Docs > Backups"
var backupFolder = DriveApp.getFolderById("foldername");
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheetId = spreadsheet.getId();
var file = Drive.Files.get(spreadsheetId);
var url = file.exportLinks[MimeType.CSV];
// UPDATE THE SHEET-NAME TO BE EXPORTED - e.g. sheetName = "Malawi Ops"
// -- LEAVE IT BLANK TO EXPORT THE ENTIRE SPREADSHEET - i.e. sheetName = ""
var sheetName = "";
if (sheetName.length) {
var sheet = spreadsheet.getSheetByName(sheetName);
var sheetId = sheet.getSheetId();
url += "&gid=" + sheetId;
}
var token = ScriptApp.getOAuthToken();
var options = { headers: { Authorization: "Bearer " + token } };
var response = UrlFetchApp.fetch(url, options);
var doc = response.getBlob();
var backupDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH-mm-ss");
var backupName = spreadsheet.getName() + ".csv";
var tempFile = DriveApp.createFile(doc).setName(backupName);
tempFile.makeCopy(backupName, backupFolder);
tempFile.setTrashed(true);
}
I understood that you are using your code to make CSV backups of your Sheets, and you want each new backup to override the old one. If I understood it well, you are very close from reaching your goal.
After copying the new backup, you only need to delete the old one. This isn't directly overwriting, but has the same result. Immediately after you created your new CSV, you can search for the old one and use File.isTrashed() on it. Please, don't hesitate to ask me any doubts.

Google Script is outputting image file path instead of image

I am working on an app for use on sites that deal with water treatment and waste management.
This app is being developed using appsheet.com
Appsheet creates apps using spreadsheets and stores all data input into the app in these spreadsheets.
I have a script that extracts data from a spreadsheet and applies it to a Google doc template.
The at the end of the template there is a signature section.
Appsheet has a built in signature feature, which stores these signatures as a png file in a subfolder.
In the spreadsheet, the image is kept as a file path, as seen here
When run, the script interprets this as the text you see and applies it to my template.
I need to be able to read this and apply the image, not the file path.
I have tried using some of the various image classes explained on the Apps Script Reference, none of which have worked for me.
function chooseRowMethod(templateId){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
var data = sheet.getRange(2, 2, 11, 18).getValues();//starting with row 2 and column 1 as our upper-left most column, get values from cells from 1 row down, and 15 columns along - hence (2,1,1,15)
var docTitle = sheet.getRange(2, 2, 11, 1).getValues();//this is grabbing the data in field B2
var docTitleTagNumber = sheet.getRange(2, 3, 11, 1).getValues();
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
today = dd + '/' + mm + '/' + yyyy;
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response){
Logger.log(i);
var row = data[i - 1];
var docId = DriveApp.getFileById(templateId).makeCopy().getId();
var doc = DocumentApp.openById(docId);
var body = doc.getActiveSection();
body.replaceText("%SITEID%", row[0]);
...
body.replaceText("%SIGNED%", row[16]);
doc.saveAndClose();
var file = DriveApp.getFileById(doc.getId());
var newFolder = DriveApp.getFolderById("16wRGBVdV0OZ5YfKhqEQSFMsux-ekGCCa");
newFolder.addFile(file);
var newDocTitle = docTitle[i - 1][0];
var newDocTagNumber = docTitleTagNumber[i - 1][0];
doc.setName(newDocTitle + " " + newDocTagNumber + " " + today);
}
}
}
}
I am aware that the body.replaceText() line does exactly as it says. I have tried using body.setImage(), but this also failed to get any results.
The expect result is picture that is taken from its folder and applied to the template using the file path that is given in the spreadsheet.
Problem
You want to insert image from Google Drive to Google Docs document body.
Solution
Use the InlineImage element (class) and corresponding appendImage() or insertImage() methods. This is done in three simple steps:
Access your image file (e.g. DriveApp.getFileById() or similar, it's up to you);
Get file data as Blob class instance via getBlob() method;
invoke appendImage() or insertImage() with blob as argument (note that insertImage() requires child element index as its first argument).
Sample
function insertImage() {
var doc = DocumentApp.create('TEST');
var body = doc.getBody();
var image = DriveApp.getFileById('yourIdHere').getBlob();
var inline = body.appendImage(image);
}
Reference
insertImage() method reference;
appendImage() method reference;
getBlob() method reference;
After spending some time on other parts of this project, I have come back to this problem and found a solution using the following code.
var signature = row[17];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alers('there is more than one file with this name' + sign);
}
var sigElectInstaller = "%SIGNELECTINSTALL%";
var targetRange = body.findText(sigElectInstaller); // Finding the range we need to focus on
var paragraph = targetRange.getElement().getParent().asParagraph(); // Getting the Paragraph of the target
paragraph.insertInlineImage(1, file.getBlob());// As there are only one element in this case you want to insert at index 1 so it will appear after the text // Notice the .getBlob()
paragraph.replaceText(sigElectInstaller, ""); // Remove the placeholder

Trying to delete named external sheets from a container script

My first time scripting, I'm trying to create an order sheet for outside sales reps, that they can fill out, and click a button to submit a template file. Sheets 0 & 1 are survey questions that fill in references on sheet 2 (Final Product). Sheets 3 & 4 are data validation fields for sheets 0 & 1 including email addresses for recipients.
I am trying to copy a spreadsheet to a new spreadsheet, --> converting a specific sheet from the copy to text so that I can delete all sheets except the converted sheet and not get reference errors --> email converted sheet via pdf format to contacts on sheet 3. My code does it all except delete the 4 sheets that I want to(0,1,3,4). The script is a container script, so whenever I try to call SpreadsheetApp.getActiveSpreadsheet(); It automatically grabs the container file and deletes the 'template' sheets. I need to know how to delete indexes 0,1,3,4 of an external spreadsheet, in a different folder of my drive.
The code below is a hodgepodge of snippets I have butchered and pieced together.
I hope you can understand all of this.
Here is what I have:
function SubmitOnClicks() {
// Set the Active Spreadsheet so we don't forget
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var orderSheet = ss.getSheetByName("Order Sheet");
var ValidationRef = ss.getSheetByName("Validation References");
orderSheet.activate();
// Set the message to attach to the email.
var message = "Please see attached";
// Get Project Name from Range B3:D3
var projectname = ss.getRange("B3").getValues();
// Get BFS Size from Range C25:E25
var size = ss.getRange("C25:E25").getValues();
// Construct the Subject Line
var subject = projectname + " " + size;
// Get contact details from "Validation References" sheet and construct To: Field
var numRows = ValidationRef.getLastRow();
var emailTo = ValidationRef.getRange(2, 12, 5, 2).getValues();
// Google scripts can't export just one Sheet that I know of
var submittalDate = orderSheet.getRange(17, 2).getValues();
var submittalName = "BFS Submittal"
var folderID = "My Drive Folder ID"; // Folder id to save Copy to: MyDrive/_Sheets/Shared/BFS Exports
var folder = DriveApp.getFolderById(folderID);
var sourceSheet = ss.getSheetByName("Order Sheet");
var sourceRange = sourceSheet.getRange(1,1,sourceSheet.getMaxRows(),sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(ss.getId()).makeCopy(submittalName, folder))
var destSheet = destSpreadsheet.getSheets()[2];
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
// Replace cell values with text (to avoid broken references)
destRange.setValues(sourcevalues);
var files = DriveApp.searchFiles(
'mimeType = "BFS Submittal' + MimeType.GOOGLE_SHEETS + '"');
while (files.hasNext()) {
var spreadsheet = SpreadsheetApp.open(files.next()); //I'm stuck after this Line
var sheet = spreadsheet.getSheets()[0, 1, 3, 4];
}
// Make the PDF, currently called "BFS Submittal.pdf"
var pdf = DriveApp.getFileById(ss.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'BFS Submittal',content:pdf, mimeType:'application/pdf'};
// Send the freshly constructed email
MailApp.sendEmail(emailTo, subject, message, {attachments:[attach]});
}

Export Google Sheets to PDF without cell gridlines

I'm trying to export PDF from Google Sheets via Apps Script. I've found useful code online, which works perfectly, except I can't find a way to export it without the grid, or change margins and page size.
function generatePdf() {
var originalSpreadsheet = SpreadsheetApp.getActive();
var sourcesheet = originalSpreadsheet.getSheetByName("TestSheet");
var sourcerange = sourcesheet.getRange('B1:K55'); // range to get - here I get all of columns which i want
var sourcevalues = sourcerange.getValues();
var data = sourcesheet.getDataRange().getValues();
var number = originalSpreadsheet.getRange('G9:H9').getValue();
var newSpreadsheet = SpreadsheetApp.create("Invoice pdf"); // can give any name.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var projectname = SpreadsheetApp.getActiveSpreadsheet();
var sheet = sourcesheet.copyTo(newSpreadsheet);
var destrange = sheet.getRange('B1:K55');
destrange.setValues(sourcevalues);
newSpreadsheet.getSheetByName('Sheet1').activate();
newSpreadsheet.deleteActiveSheet();
var invoiceName = "Invoice "+number;
var pdf = DriveApp.getFileById(newSpreadsheet.getId());
var theBlob = pdf.getBlob().getAs('application/pdf').setName(invoiceName);
var folderID = "1Y7n1e_tzQWvzVykHJf_DSm9lBVmokDHA"; // Folder id to save in a folder.
var folder = DriveApp.getFolderById(folderID);
var newFile = folder.createFile(theBlob);
DriveApp.getFileById(newSpreadsheet.getId()).setTrashed(true);
}
I've been looking for answers everywhere, but I cannot apply some of the solutions I find to my code.
I'm not sure how to do it with the code, but if you want to do it without code... when downloading from Google Sheets, you have the option to remove the cell gridlines. Here are the directions:
File > Download > PDF Document (.pdf)
This will open a preview screen. Sidebar on the right has a dropdown menu called "Formatting."
Under "Formatting," uncheck the box labeled "Show gridlines."
Thats it!
It’s not possible to pass options into the getAs function, but you can export the file yourself and download the URL:
function getPdf(spreadsheet) {
var options = {
format: 'pdf',
exportFormat: 'pdf',
size: 7, // A4
portrait: true,
gridlines: false,
};
// construct export URL
var query = Object.keys(options).map(function (key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(options[key]);
}).join('&');
var exportUrl = spreadsheet.getUrl().replace(/\/edit.*$/, '/export?' + query);
var response = UrlFetchApp.fetch(exportUrl, {
headers: {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken(),
},
});
return {
fileName: spreadsheet.getName() + '.pdf',
content: response.getBlob().getBytes(),
mimeType: MimeType.PDF,
};
}
The full list of export options can be found at https://stackoverflow.com/a/46312255.

Google Apps Script - Using URL of File in Drive in =IMAGE() Spreadsheet Formula

In the below code, I'm getting the URL of a file in my Drive and using that in the formula =IMAGE(). However, the image isn't being displayed in the cell. I copied and pasted the URL that was being retrieved into my browser and it pulls up the image file. I also tried entering a different URL (from a Google image search) and it displayed the image in the cell. Here is a snippet of my code that isn't working:
//Function to populate Packing Instructions sheet
function createPackingInstructions() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entryFormSheet = ss.getSheetByName('Entry Form');
var packingInstructionsSheet = ss.getSheetByName('Packing Instructions');
var poNumber = entryFormSheet.getRange(2, 2).getValue();
var drive = DriveApp;
var proofHorizontal = drive.getFilesByName('PO ' + poNumber + ' Proof Horizontal.png');
var proofRange = packingInstructionsSheet.getRange(1, 7);
Logger.log(poNumber);
//Starts by clearing the Instructions sheet
packingInstructionsSheet.getRange(11, 1, 30, 11).clear();
proofRange.clearContent();
Logger.log(proofHorizontal.hasNext());
//Gets image file URL
while (proofHorizontal.hasNext()) {
var file = proofHorizontal.next();
var proofName = file.getName();
var proofUrl = file.getUrl();
Logger.log(proofName);
Logger.log(proofUrl);
proofRange.setFormula('IMAGE("' + proofUrl + '", 1)');
}
}
I adjusted the code based on the advice in here to use the permalink version of the URL, but it has the same behavior; it inputs the formula correctly and the URL works when entered into my browser, but the image won't display in the cell. Here is the updated code:
//Function to populate Packing Instructions sheet
function createPackingInstructions() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entryFormSheet = ss.getSheetByName('Entry Form');
var packingInstructionsSheet = ss.getSheetByName('Packing Instructions');
var poNumber = entryFormSheet.getRange(2, 2).getValue();
var drive = DriveApp;
var proofHorizontal = drive.getFilesByName('PO ' + poNumber + ' Proof Horizontal.png');
var proofRange = packingInstructionsSheet.getRange(1, 7);
var baseUrl = "http://drive.google.com/uc?export=view&id=";
Logger.log(poNumber);
//Starts by clearing the Instructions sheet
packingInstructionsSheet.getRange(11, 1, 30, 11).clear();
proofRange.clearContent();
Logger.log(proofHorizontal.hasNext());
//Gets image file URL
while (proofHorizontal.hasNext()) {
var file = proofHorizontal.next();
//file.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
var proofId = file.getId();
var proofUrl = baseUrl+proofId;
Logger.log(proofUrl);
proofRange.setFormula('IMAGE("' + proofUrl + '", 1)');
}
}
Necro-ing my own questions, since I found a working solution:
My second code example above was close, but you need to use the "download" version of the URL, not the "view" version. See below:
var baseUrl = "https://drive.google.com/uc?export=download&id=";
//Gets image file URL
while (proofHorizontal.hasNext()) {
var file = proofHorizontal.next();
//This line may be necessary, depending on permissions
//file.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
var proofId = file.getId();
var proofUrl = baseUrl+proofId;
}
Here is a link to some info on this: https://blog.appsevents.com/2014/04/how-to-bypass-google-drive-viewer-and.html