I programmed one that calls a Google Doc and in said Doc there is a variable called #Name# this is replaced by the name that is stored in a SpreadSheet.
My question is that to that Doc I want to add a variable called # Photo # and it is replaced by the photo stored in a Drive URL and this URL is in a SpreadSheet
How can I call that URL that is in Spreadsheet and be able to replace the field #photo#
With the name it does well with the function
body.replaceText ('#name#', name);
but with the image I don't know what to call it
Since #Photo# is a paragraph of its own, you can do the following:
Using the Document Body, find the text #Photo# via findText(searchPattern) and get the Element that corresponds to this text via getElement().
Once you have retrieved this element, you can remove the text (#Photo#), since the element itself will exist even if the text is an empty string. You can use setText("") for that.
Get the Blob from the image URL via UrlFetchApp.fetch(url) and getBlob().
Get the retrieved element's parent, which is the paragraph #Photo# was part of, and use insertInlineImage to insert the retrieved image to the beginning of this paragraph.
Code sample:
function replaceTextWithImage() {
var body = DocumentApp.getActiveDocument().getBody(); // Get current document's body
var element = body.findText("#Photo#").getElement(); // Get element of #Photo#
element.asText().setText(""); // Remove #Photo# placeholder
var url = "YOUR_IMAGE_URL"; // Change this accordingly
var blob = UrlFetchApp.fetch(url).getBlob(); // Get blob from image URL
var image = element.getParent().asParagraph().insertInlineImage(0, blob); // Insert image
}
Notes:
I'm assuming that your question was only about inserting the image to the Document, not about the previous steps of populating the spreadsheet, retrieving the image URL, etc.
This sample refers to a script that is bound to the Google Doc (hence, it's using getActiveDocument() to retrieve the Doc). If that's not the case, you should retrieve the document via openById(id) or openByUrl(url).
Related
Good afternoon all;
I have a google spreadsheet which is populated by a Qualtrics form, where the user can upload a photo.
Using Apps Script, I have created a CRUD database that is accessed via a google sites page (html form). All is working great, but I'd like to display the photo that the user uploaded in their record; the URL to the image is saved in a column on the spreadhsheet.
The thing is, I'm not a very worthy 'coder' and I can't figure out how to make this happen.
I'm accessing my data like this: Note that I have removed a good portion of the code to save space.
Code.gs
function getFormValues(formObject) {
if (formObject.responseID && checkID(formObject.responseID)) {
var values = [[formObject.responseID.toString(),
formObject.permitNumber,
formObject.dateOfIssue,
formObject.photoLink, <<<this is where the URL would be pulled in. Column Q in the spreadsheet.`
DataTable.html
function populateForm(records){
document.getElementById('responseID').value = records[0][0];
document.getElementById('permitNumber').value = records[0][1];
document.getElementById('photoLink').value = records[0][16];
...I just don't know what to do after this point...
I did some tests, and I have an option for you to use Google Apps Script and the formula IMAGE.
Since I didn't have much information on your setup, like where the files are uploaded, which is the format of the URL, etc. I made a test environment using Google Forms, the files uploaded to Google Drive, and Apps Script.
With the formula:
=IMAGE("URL", [mode], [height], [width])
For the test, I use mode 4, which allows you to specify the size of the image. You can use either of the modes:
1 resizes the image to fit inside the cell, maintaining the aspect ratio.
2 stretches or compresses the image to fit inside the cell, ignoring the aspect ratio.
3 leaves the image at its original size, which may cause cropping.
4 allows the specification of a custom size.
The height and width needs to be in pixels.
When you use a Google Form to upload an image, you get an URL with the following format:
https://drive.google.com/open?id=[ID_OF_THE_FILE]
There is a trick to display images from Google Drive into Google Sheets using the image formula. You need the URL with the format: (I got this idea from this post)
https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]
So the script that I created modifies the URL in the column of the image from:
https://drive.google.com/open?id=[ID_OF_THE_FILE]
To:
=IMAGE("https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]", 4, 60, 60)
And place it in the cell in the column. However, you can place it in the same column as the current URL too.
Here is the sample code:
function testImage() {
// Identifies the sheet link to the Google Sheet and the tab you will be working with
// you can also use const sheet = SpreadsheetApp.getActiveSpreadsheet()
// .getSheetId("[ID_OF_THE_SHEET]");
// if the Script is not bound to the sheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
// Gets the range and the values where the URLs of the images are located.
// in my example there are in column B
let range_1 = sheet.getRange("B2:B").getValues().flat();
// Filters the rows without values or empty
range_1 = range_1.filter((element) => {return (element !== '')});
// Start of the loop to run on each row with URLs
for (let i=0; i< range_1.length ; i++){
let row = range_1[i];
// For this sample, I replace the URL with the word "Done" to prevent duplicates later on in the Script
// This can be skipped or replaced place, in a different location.
if (row != "Done"){
// Replace the URL from https://drive.google.com/open?id=[ID_OF_THE_FILE]
// to =IMAGE("https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]
let temp_imageURL = row.toString().replace('https://drive.google.com/open?id=',
'=IMAGE("https://lh3.googleusercontent.com/d/');
// However, the process is not complete yet, we need to complete the formula
// so we added the last part ",4,60,60) to the URL
// making it "https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]",4,60,60)
let imageURL = temp_imageURL+'",4,60,60)';
// place the complete formula in the current iteration, row 3 (which is row C)
sheet.getRange(i+2,3).setValue(imageURL);
// replace the current URL in Row 2 (which is row B) with the word "Done"
sheet.getRange(i+2,2).setValue("Done");
}
}
}
It looks like:
Reference:
IMAGE
Replace
Is there a canonical way to set an ID—or other searchable, persistent data attribute—on Elements in Google Docs, such that I can easily refer back to it later? I'm looking for something equivalent to getElementById in javascript. Almost all examples I've seen, including Google's own docs, seem to reference objects by searching for text strings or inserting new strings.
I've found one reference in the NamedRanges class to a getId function, but I can't find any place to set that ID. I do see the setAttributes function on Elements but that seems to apply only for pre-defined attribute types. I haven't tested that, though.
In case it's relevant: my interest is in automatically creating a document from a Google Sheet and populating based on the current values in the sheet. I'd like to assign specific Elements individual IDs so I can easily retrieve the Element and replace the text if the values in the sheet change later on.
Turns out that this is possible using NamedRanges, I just didn't read carefully enough.
Note: All the following examples are working off this Google doc. You can make a copy and select "Script Editor" from the Tools menu to see the code.
You can assign named ranges pretty easily using Apps Script. The below code looks through the doc for [[TITLE]] and [[ABSTRACT]] and assigns named ranges to those chunks. Note that in the aforelinked doc I put them in a table to avoid issues with partial ranges.
function assignNamedRanges() {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();
const placeholders = ["title", "abstract"];
placeholders.forEach(p => {
const rangeBuilder = doc.newRange();
const text = body.findText("[[" + p.toUpperCase() + "]]");
rangeBuilder.addElement(text.getElement());
doc.addNamedRange(p, rangeBuilder.build());
});
}
Once you assigned them, you can update the range to something else in a separate function:
function updateNamedRanges() {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();
const title = doc.getNamedRanges("title")[0];
const abstract = doc.getNamedRanges("abstract")[0];
title.getRange().getRangeElements()[0].getElement().asText().setText("Bob");
abstract.getRange().getRangeElements()[0].getElement().asText().setText("I like pancakes");
}
Note that NamedRanges are persistent, and the multiple NamedRange instances can have the same name. This means that if you run the first function four times, you'll have eight named ranges. You can make a convenience function to clear all those out pretty easily:
function clearNamedRanges() {
DocumentApp.getActiveDocument().getNamedRanges().forEach(r => {
r.remove();
})
}
So I been checking the documentation about elements for Google Docs in AppScript and it seems that some of them can be modified but not as freely as it looks as noted in the documentation:
Elements shown in bold can be inserted; non-bold elements can only be manipulated in place.
I tried checking with setAttributes as you mentioned however the attributes itself can only be from a document elements like: TEXT, PARAGRAPH, TABLE, ETC, this elements can't receive an ID as there is not method to insert an specific ID as you are requiring, most of the values that can be inserted are specific element attributes like: Font size, Font family, etc.
Question:
I am trying to avoid looping through the document until I find the tag and then grabbing the parent. Does anyone know if there is a way to find a google doc element by an id or handle?
Context:
I have a script that opens a defined template doc; copies it into a new document element by element, and then replaces any mustache tags with the values from a passed in object using the replaceText function. This part works fine, but there is one section where I would like to repeat a table row based on the object value being an array. Unfortunately I can not seem to figure out how to locate the table in the template doc other than something like:
if('{{ some tag }}' in aElement){
var repeatableElement = aElement.getParent();
if(repeatableElement){
goRunRepeatFunction();
}
}
Just seems like there should be a way to jump directly to an element by id.
Your task can be accomplished by using this snippet:
function getElement() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var numOfElements = body.getNumChildren();
var element = body.getChild(INDEX_OF_THE_CHILD).asText();
console.log(element.getText());
}
This gathers the body of the document and retrieves the number of children it has. This number represents the elements the document has. So, in order to access a certain element, you just have to make use of the getChild(index) method
Note
Please bear in mind that the getNumChildren() method will end up returning the number of all the elements in the document, including empty lines - these will be considered elements as well.
Usually, when working with a document in Apps Script, the elements are retrieved by their types in order to preserve them. So for example, if you have images in your document, it'd be best to retrieve those by using the getImages() and this will end up returning a list of containing all the images in the document, with each image being accessible by their index.
Another method would be to retrieve all the paragraphs in the body by using the getParagraphs() method, but if you know the exact structure of your document, you can retrieve all the elements by their type, and then access them as such.
Reference
Class ContainerElement - getNumChildren();
Class ContainerElement - getChild(Integer);
Class Body - getParagraphs().
I'm very new using GAS what I'm trying to do is to copy some information that is already on the bottom of my doc to anywhere I want in my docs, this should work just by copying the information and paste it at the place I desire, but I want it to be done with Google App Script because it's a daily task and it's easier to do it with a function, instead of copying and pasting manually. Searching on how to do this, I found a lot of information about how to do it on Spreadsheets, but I needed it to be done on Google Docs. How can I do that?
If someone can guide me or send me a link to another similar question that would be very helpful, I don't know where to start.
This is what I have until now, I get all the data of the current doc and set it again to the page, the code gives me problems because it deletes my other information, also it selects all the doc's information. I want to select a piece of specific information and don't copy the content style.
function copyPasteInfo() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var notesText = body.getText();
body.appendPageBreak();
body.setText(notesText);
}
Link to the doc document
https://docs.google.com/document/d/1s2TCspXbjvHVurwhIWSdwJ_hMcZIoLTKj4FAB82nmhM/edit
Video example of how what i want to do
https://www.screencast.com/t/UmEon8Fm0lPe
Picture of the information i'm trying to copy and paste to the bottom of my doc
If I correctly understood your question, this code will help you to achieve your goal.
let body = DocumentApp.getActiveDocument().getBody();
function moveTable() {
// Get the last table and the previous table found in your Doc
const [previousTable, bottomTable] = getDesireTables();
// Make a copy of your last table
const bottomTableCopy = bottomTable.copy();
// Get the previous table's index
const previousTableIndex = body.getChildIndex(previousTable);
// Insert the last table's copy under the previous table in your Doc
body.insertTable(previousTableIndex + 1, bottomTableCopy);
// Remove the original last table
body.removeChild(bottomTable);
}
function getDesireTables(){
const tablesArr = body.getTables().slice(-3);
// Get the parent element type to check if it's a cell
const parentELementType = tablesArr[tablesArr.length - 1].getParent().getType();
if(parentELementType === DocumentApp.ElementType.TABLE_CELL){
// If there's a table inside a table, return this
return tablesArr.slice(0, 2);
}
else{
return tablesArr.slice(-2);
}
}
What I did was to get the last two tables in the Doc, then I made a copy of the last one and with the index of the previous one, I inserted it under the previous one.
Edit
I noticed you had a table inside a table. therefore I added the getDesireTables function. Which it will check if your bottom table has a table inside.
Docs
These are the docs I used to help you:
getTables().
copy().
insertTable(childIndex, table).
I can extract the body of an email but I can not find a way to search that body. findText only works on documents and I haven't found a way to put an email into a document. The search in email only returns the whole email, which I already have. Am I looking at this the wrong way or have I missed something?
Assuming you can get the body of the email as a string, here is something you could try:
var doc = DocumentApp.getActiveDocument();
// Use editAsText to obtain a single text element containing
// all the characters in the document.
var text = doc.editAsText();
// Insert text at the beginning of the document.
text.insertText(0, 'Your Text To Insert.\n');
//Find search for your results
text.findText(searchPattern)
I am not familiar with Google-Script but I hope this could possibly point you in the right direction.
https://developers.google.com/apps-script/class_text