We have a column that contains links to PDFs that starts on line 4 (e.g B4:B). I am trying to find a way to automatically download the PDF files that are accessed via the links to a folder on Drive. This is what I have so far:
function savePDFs() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 3; i < data.length; i++) {
Logger.log(data[i][1]);
}
}
Presumably the above code would write the links starting in column B (index value of [1]) on row 4 (i value of 3) (ie., B4) until the bottom of the data set (eg., data.length()).
I'm now confused about how to access and save the PDF link that are written in the logger to a folder.
Would someone be willing to help me out? I'm currently having to go to each link, click Save Link As... and then navigate to the folder that I'd like to save the linked PDF to. My hope is to modify the above process using code.
Update: I found this bit of code here that may help me out. Note, I changed the PDF link to a currently valid PDF link.
var urlOfThePdf = 'http://download.p4c.philips.com/l4b/9/929000277411_eu/929000277411_eu_pss_aenaa.pdf';// an example of online pdf file
var folderName = 'GAS';// an example of folder name
function saveInDriveFolder(){
var folder = DocsList.getFolder(folderName);// get the folder
var file = UrlFetchApp.fetch(urlOfThePdf); // get the file content as blob
folder.createFile(file);//create the file directly in the folder
}
Okay, I'm going to go and noodle with the data that is in the logger to confirm that the data is in properly formatted PDF links, then I'm going to test this new bit of code out. I feel like I'm getting close.
You can't force a download of a file from an apps script, you must try that from an HTMLService and not sure it will work.
For your need I would recommend to create a dedicated folder and you add all the pdf in it and you use the download function of the drive interface to download all files in one clic.
In drive, a file can be put in several folders so the pdf files stay in the original folder but you create a new folder 'PDF for download" for example and you put them in it. To do that from drive interface you have to click on "shift"+Z when file(s) is/are selected.
For you current list of file you just have to add in your loop the add to folder function. You can use this function.
function addFileToFolder(id){
var folderPDF = DriveApp.getFolderById("Id OFFolder to put pdf");
var file = DriveApp.getFileById(id);
folderPDF.addFile(file);
}
EDIT : Function will browse list of url, get the file and make a copy in a dedicated folder on the user drive.
function downloadInDriveFolder(){
var folderID = 'Id of the folder';// put id of the folder
var folder = DriveApp.getFolderById(folderID)// get the folder
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 3; i < data.length; i++) {
var blob = UrlFetchApp.fetch(data[i][1]).getContent();
var pdf = DriveApp.createFile(blob);
pdf.setName(data[i][0]);//Put as name of the file the value in col A
folder.addFile(pdf);
}
}
Well I figured it out. I was expecting more code, but this does it for me:
function listPDFs() {
var out = new Array();
var row = 3; //row index of 0 = row 1
var column = 4; // column index of 0 = column A
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var folder = DriveApp.getFolderById("this is where you paste your folder id"); // destination folder (this is the 0978SDFSDFKJHSDF078Y98hkyo looking value when you right click your folder and select "Get Link")
for (var i=row ; i<data.length ; i++) {
if(data[i][column] !== "") {
var file = UrlFetchApp.fetch(data[i][column]);
folder.createFile(file);
}
}
return
}
As you can see, I included a row and column variable so that I could easily change these.
I haven't figured out how to assemble them into a merged PDF, but I did figure out that I could sort them by date (which places the top most item first) and then right click and select "Open With...PDF Mergy", which then moves the PDFs into PDF Mergy and merges them up in the correct order. You can find PDF Mergy in the Chrome App Store. If I figure out how to automatically call PDF Mergy from GAS, I'll post that up--but for the time being the above code has saved us a ton of time...so I'm calling it good enough for the time being.
Related
I am generating QR codes in a column in Google Sheets with data entered via a Google Forms, and I would like to save them as images.
Using Ctrl + C and Ctrl+V works only inside the Google Sheet and trying to copy and paste that one outside the sheet doesnt work either. The clipboard is detected as not containing images to paste elsewhere.
The problem is not in generating the QR, but exporting or saving it as image.
To generate the QR's I'm using the api.qrserver, but the documentation for this only goes about creating or reading QR's [http://goqr.me/api/doc/] , for which this base is used to technically generate a PNG image:
http(s)://api.qrserver.com/v1/create-qr-code/?data=[URL-encoded-text]&size=[pixels]x[pixels]
However, If I use that formula externally to link directly to the cell, the PNG created only gives the URL address of the Google Sheet and not the information of the QR.
This is the array formula I'm using at row 1 to generate the QR's:
={"QR";arrayformula( if(
len(INDIRECT("A2:A")),IMAGE("https://api.qrserver.com/v1/create-qr-
code/?size=120x120&data="&ENCODEURL(
"-Tip: "&indirect("B2:B")& char(10)&
"-Ub: "&INDIRECT("C2:C")& char(10)&
"-#: "&indirect("D2:D")& char(10)&
"-IDE: "&INDIRECT("E2:E")& char(10)&
"-Serial: "&INDIRECT("G2:G") & char(10)&
"-Pa: "&indirect("H2:H") & char(10)&
"-IP: "&INDIRECT("I2:I") & char(10)&
"-As: "&indirect("J2:J") & char(10)&
"-R: "&INDIRECT("K2:K") & char(10)&
"-Ar: "&indirect("L2:L") & char(10)
),2)
,""))}
Ideally, I would like to automatically make the QR's as images and save them to a folder in google drive automatically. If possible, assigning them an IDE value that is contained in another column as well as in the same QR code.
Here is the example for the QR generation:
[https://docs.google.com/spreadsheets/d/16pALQ0LNbBqqdRNNejar8zJJPPNEP8I7HSbKDOJHZaw/edit?usp=sharing ]
You want to save the QR codes in the Spreadsheet as the image files using Google Apps Script.
In this case, you want to achieve this without changing the Spreadsheet.
Values are put to from the column "A" to "L", when the form was submitted.
If my understanding is correct, how about this sample script? Please think of this as just one of several answers.
In this sample script, the QR code is directly retrieved from api.qrserver.com and those are saved as the image files, because in your situation, the images cannot be retrieved from the Spreadsheet. In this case, all parameters of QR code are used from the Spreadsheet.
Sample script:
Before you run the script, please set the variables of sheetName and folderId. When you run this script, all QR codes in the sheet of Hoja_calculos are created as the image files.
function myFunction() {
var sheetName = "Hoja_calculos"; // Sheet name is from your shared Spreadsheet.
var folderId = "###"; // Folder ID of the folder where the image files are created.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sheetName);
var url = "https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=";
var keys = ["-Tipo:", "-UbicaciĆ³n:", "-# registro:", "-ID-C:", "-# Serial:", "-Pas:", "-Di I:", "-Asi:", "-RP:", "-Ar:"];
var values = sheet.getRange(2, 2, sheet.getLastRow() - 1, 11).getValues()
.filter(function(e) {return e.some(function(f) {return f})})
.map(function(row) {
row.splice(4, 1);
return row;
});
var length = values.length;
// values = [values.pop()]; // If you want to save only the QR code of the last row, please use this line.
var reqs = values.map(function(e) {return {url: url + encodeURIComponent(keys.map(function(f, j) {return f + e[j]}).join("\r"))}});
var res = UrlFetchApp.fetchAll(reqs);
res.forEach(function(r, i) {
var blob = r.getBlob().setName(length == values.length ? "row" + (i + 2) + ".png" : "row" + (length + 1) + ".png");
DriveApp.getFolderById(folderId).createFile(blob);
});
}
Note:
If you want to save only the QR code of the last row, please use this line. At that time, please remove // of // values = [values.pop()];. By this, only the QR code of the last row is retrieved.
Because I thought that above situation might be required for your situation, I added it. For example, by using the installable trigger to the function, when the form was submitted, only QR code of the last row is created as the image file.
But I'm not sure about your environment of the form. So I'm not sure whether this can be used for your situation. I apologize for this.
I confirmed that this script worked for your shared Spreadsheet. If the actual Spreadsheet is different from this, please modify the script, because it might not work fine.
References:
Class UrlFetchApp
Class DriveApp
You can not use a custom function and interacts with Google Drive, there is a limitation to access user data using custom function and onEdit function on G script:
https://developers.google.com/apps-script/guides/sheets/functions
AFAIK the only way to achieve this is creating a custom menu and interacts with a custom function by menu.
var url = "https://api.qrserver.com/v1/create-qr-code/?data=Sample&size=128x128";
var folders = DriveApp.getFoldersByName("QRCodeFolderName");// Put here the name of the folder inside root folder
var folder = folders.next();
var response = UrlFetchApp.fetch(url);
if (response.getResponseCode() == 200){
var fileBlob = response.getBlob();
folder.createFile(qrcodevalue+".png",fileBlob);
}
I have a apps script bound to a spreadsheet that creates a pdf file from the sheet. this creates one single page pdf and saves it in a folder in drive. Up until recently, this worked perfectly. Now every time I run the code it does what it is supposed to but the file has a second page that is blank. When I create the pdf manually via file/download as/pdf doc, it creates the pdf as it should, with only one page. I have tried this with both the original and copy that the script temporarily creates. Both work when done manually. I am looking for some suggestions on what could have gone wrong and what to change. Here is an example of the code:
function makePDF() {
var sheet1 = SpreadsheetApp.getActive().getSheetByName('eTimesheet');
var sheet2 = SpreadsheetApp.getActive().getSheetByName('Time Sheet');
var sheet3 = SpreadsheetApp.getActive().getSheetByName('Data');
var triggercell3 = sheet1.getRange('M33').getValue();
if (triggercell3 == 'GO'){
var techNumber = sheet3.getRange('B5').getValue();
var date = sheet3.getRange('B3').getValue();
var fileID = sheet3.getRange('B7').getValue();
var pdfName = "TimeSheet- "+ techNumber + " " + date
var ss = SpreadsheetApp.getActive();
var folder = DriveApp.getFolderById(fileID);
sheet2.showSheet();
sheet1.hideSheet();
//Copy whole spreadsheet
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(ss.getId()).makeCopy("tmp_convert_to_pdf", folder))
//save to pdf
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
sheet1.showSheet();
sheet2.hideSheet();
sheet1.getRange('M33').clearContent();
}
}
thanks for any assistance...
Without an example, first question I have is... if you delete a few rows from the sheet and run the script, are you back to 1 page?
I am asking in case the issue is just margin settings. If this is the issue, maybe you can adjust rows or use the UrlFetchApp.fetch approach as PDF page formatting can be specified (eg: margin size).
Recently I had asked THIS QUESTION to be able to save all the images present in a PDF file on the File System and I was able to save the images successfully.
I tested my code on a lot of pdf files and it ran just fine. But, today I came accross THIS pdf file from where it is not able to extract some images(attached below).
Can anyone please tell me what else I can do to extract these images? Is it even possible to extract them? Are they really images or something else? I would really appreciate the help.
My code(Please ignore the hardcoding as I am still testing this out):
function fn_getAllImages()
{
var strPdf = "C:\\Users\\a614923\\Desktop\\haka\\Work\\2017\\10. October\\31\\test.PDF";
var strout = "C:\\Users\\a614923\\Desktop\\haka\\Work\\2017\\10. October\\31\\Newfolder\\img"
intPage = 2; //for the 2nd page(the image is present in the 2nd page)
var objPdf = JavaClasses.org_apache_pdfbox_pdmodel.PDDocument.load_3(strPdf);
var objPage = objPdf.getDocumentCatalog().getAllPages().get(intPage-1);
var objImages = objPage.getResources().getXObjects().values().toArray();
var objImage, objImgBuffer, objImageFile;
for(var i=0; i<objImages.length; i++)
{
objImage = objImages.items(i);
Log.Message(objImage.toString());
if(aqString.Find(objImage.toString(),"PDXObjectForm",0,false)>0)
{
continue;
}
else
{
objImage.write2file_2(strout+i);
//objImgBuffer = objImage.getRGBImage();
//objImageFile = JavaClasses.java_io.File.newInstance(strout+i+".png");
//JavaClasses.javax_imageio.ImageIO.write(objImgBuffer,"png",objImageFile);
}
}
}
The image in the PDF file which I want to save(the one inside the red box below):
I want to save a Google Doc file as a pdf in the same Google Drive folder as my current file. I know I can download the file as a pdf, but then I have to upload it into the same Google Drive folder. I am trying to skip the upload step.
I have created a script to accomplish all of this, but I cannot get the images and drawings to be included in the resulting pdf.
Here is my code:
function onOpen() {
// Add a custom menu to the spreadsheet.
var ui = DocumentApp.getUi();
var menu = ui.createAddonMenu();
menu.addItem('Save As PDF','saveToPDF')
.addToUi();
}
function saveToPDF(){
var currentDocument = DocumentApp.getActiveDocument();
var parentFolder = DriveApp.getFileById(currentDocument.getId()).getParents();
var folderId = parentFolder.next().getId();
var currentFolder = DriveApp.getFolderById(folderId);
var pdf = currentDocument.getAs('application/PDF');
pdf.setName(currentDocument.getName() + ".pdf");
// Check if the file already exists and add a datecode if it does
var hasFile = DriveApp.getFilesByName(pdf.getName());
if(hasFile.hasNext()){
var d = new Date();
var dateCode = d.getYear()+ "" + ("0" + (d.getMonth() + 1)).slice(-2) + "" + ("0" + (d.getDate())).slice(-2);
pdf.setName(currentDocument.getName() + "_" + dateCode +".pdf");
}
// Create the file (puts it in the root folder)
var file = DriveApp.createFile(pdf);
// Add to source document original folder
currentFolder.addFile(file);
// Remove the new file from the root folder
DriveApp.getRootFolder().removeFile(file);
}
Is there another way to create the pdf, save to the current Google Drive folder, and not lose the images?
UPDATE
I just tested and realized that even if I export as a pdf, the images and drawings aren't included. There has to be a way to do this.
UPDATE 2
I have been testing some more and have learned a few things:
Images in the header/footer are included if they are In line, but if I use Wrap text or Break text they are not.
Images in the body can be any of the three
However, if I use the "Project Proposal" template, they include an image in the footer with Break text and it exports to pdf. I can't tell why their image is any different.
I don't want to use In line because I want the image to touch both sides of the page and In line will always leave at least 1 pixel to the left of the image.
I have a simple form and body field. when user upload an attachment I want to remove any attachment already in the body field of the document. how can I do this in my save button.
I have tried to set the properites on the fileUpload control to always change the filename to tha same name but this does not replace the file, instead it adds a new file and add a new sequential number to it
<xp:fileUpload id="fileUpload1" value="#{userdoc.Body}" filename="profile" seUploadname="false"></xp:fileUpload>
I have also tried to loop all embedded attachments in body field before before save, and all attachments are then removed, but my new attachment is not added.
A little late, but in case anyone gets here just as I did through Google...
There's also another option: a NotesXSPDocument.getAttachmentList("rtitem") returns a list of attachments in the document. Each entry is of type DominoDocument.AttachmentValueHolder which has a getState() property that returns if a file was already in the document or just added. To remove the file that was already in the document you could add this SSJS code to a save button (assuming "files" is the name of the richtext item holding the files):
var attList = docFile.getAttachmentList("files");
for(var i=0; i<attList.size(); i++) {
var att = attList.get(i);
if (att.getState()==0) { //STATE_INDOCUMENT: this is the 'old' file: remove it
docFile.removeAttachment("files", att.getName() );
} else if (att.getState()==1) { //STATE_ADDED: this is the new file
//leave it
}
}
This can be done by removing all attachments first (use the removeAllAttachments function from NotesXSPDocument object) and then attach the uploaded file manually to the richtext item.
This SSJS code has to be added to beforeRenderResponse-event of your XPage / Custom Control:
var con = facesContext.getExternalContext();
var request:com.sun.faces.context.MyHttpServletRequestWrapper = con.getRequest();
var map:java.util.Map = request.getParameterMap();
var fileDataName = getClientId('fileUpload1') ;
var fileData:com.ibm.xsp.http.UploadedFile = map.get( fileDataName );
if( fileData == null ){
return;
}
var tempFile:java.io.File = fileData.getServerFile();
var correctedFile = new java.io.File( tempFile.getParentFile().getAbsolutePath() +
java.io.File.separator + fileData.getClientFileName() );
var success = tempFile.renameTo(correctedFile);
try{
document1.removeAllAttachments("Body");
}catch(e){}
var rtFiles:NotesRichTextItem = null;
if(!(document1.getDocument().hasItem("Body"))){
rtFiles = document1.getDocument().createRichTextItem("Body")
}else{
rtFiles = document1.getDocument().getFirstItem("Body");
}
rtFiles.embedObject(lotus.domino.local.EmbeddedObject.EMBED_ATTACHMENT, "",
correctedFile.getAbsolutePath(), null);
correctedFile.renameTo(tempFile);
document1.save();
The richtext item where the files are stored is "Body", the name of the datasource is "document1". The Fileupload control is "fileUpload1".
There are two limitations:
You cannot use this with a save button, you have to use a normal button / link which makes a full refresh (see what Serdar Basegmez wrote above)
You have to refresh the page after a upload once again, otherwise you will not see the uploaded file in a file download conrol (maybe a partial refresh to this control may work).
EDIT:
Sorry, my fault: You can use this code above in a submit button that saves the document. Then you don't have to refresh the page manually!