Google Spreadsheet conditional formatting script - google-apps-script

I am trying to figure out how to use conditional formatting on a google spreadsheet similar to what you can do in excel via a formula.
I want cell A2 to change to Green if cell O2 has a value of "X" and this will be done on both columns all the way down. I know this will require a script.
I ran across a link that is similar but i do not know how to adjust it to meet my needs. Is this something that can be done?
Link: https://webapps.stackexchange.com/questions/16745/google-spreadsheets-conditional-formatting

Here's a script you could use to do what you described:
function formatting() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var columnO = sheet.getRange(2, 15, sheet.getLastRow()-1, 1);
var oValues = columnO.getValues();
for (var i = 0; i < oValues.length; i++) {
if (oValues[i][0] == 'X') {
sheet.getRange(i + 2, 1, 1, 1).setBackgroundColor('green');
}
}
}

In the new Google sheets, this no longer requires a script.
Instead, in conditional formatting, select the option "custom formula", and put in a value like =O2="X" - or indeed any expression that returns a boolean true/false value.
From what I can tell, the references listed in these custom scripts are a bit weird, and are applied as follows...
If it's a cell within your selected range, then it is changed to "the cell that's being highlighted".
If it's a cell outside your selected range, then it's changed to "that position, plus an offset the same as the offset from the current cell to the top left of the selected range".
That is, if your range was A1:B2, then the above would be the same as setting individual formatting on each cell as follows:
A1 =O2="X"
A2 =O3="X"
B1 =P2="X"
B2 =P3="X"
You can also specify fixed references, like =$O$2="X" - which will check the specific cell O2 for all cells in your selected range.

(Feb 2017) As mentioned in another answer, Google Sheets now allows users to add Conditional Formatting directly from the user interface, whether it's on a desktop/laptop, Android or iOS devices.
Similarly, with the Google Sheets API v4 (and newer), developers can now write applications that CRUD conditional formatting rules. Check out the guide and samples pages for more details as well as the reference docs (search for {add,update,delete}ConditionalFormatRule). The guide features this Python snippet (assuming a file ID of SHEET_ID and SHEETS as the API service endpoint):
myRange = {
'sheetId': 0,
'startRowIndex': 1,
'endRowIndex': 11,
'startColumnIndex': 0,
'endColumnIndex': 4,
}
reqs = [
{'addConditionalFormatRule': {
'index': 0,
'rule': {
'ranges': [ myRange ],
'booleanRule': {
'format': {'textFormat': {'foregroundColor': {'red': 0.8}}}
'condition': {
'type': 'CUSTOM_FORMULA',
'values':
[{'userEnteredValue': '=GT($D2,median($D$2:$D$11))'}]
},
},
},
}},
{'addConditionalFormatRule': {
'index': 0,
'rule': {
'ranges': [ myRange ],
'booleanRule': {
'format': {
'backgroundColor': {'red': 1, 'green': 0.4, 'blue': 0.4}
},
'condition': {
'type': 'CUSTOM_FORMULA',
'values':
[{'userEnteredValue': '=LT($D2,median($D$2:$D$11))'}]
},
},
},
}},
]
SHEETS.spreadsheets().batchUpdate(spreadsheetId=SHEET_ID,
body={'requests': reqs}).execute()
In addition to Python, Google APIs support a variety of languages, so you have options. Anyway, that code sample formats a Sheet (see image below) such that those younger than the median age are highlighted in light red while those over the median have their data colored in red font.
PUBLIC SERVICE ANNOUNCEMENT
The latest Sheets API provides features not available in older releases, namely giving developers programmatic access to a Sheet as if you were using the user interface (conditional formatting[!], frozen rows, cell formatting, resizing rows/columns, adding pivot tables, creating charts, etc.).
If you're new to the API & want to see slightly longer, more general "real-world" examples of using the API, I've created various videos & related blog posts:
Migrating SQL data to a Sheet plus code deep dive post
Formatting text using the Sheets API plus code deep dive post
Generating slides from spreadsheet data plus code deep dive post
As you can tell, the Sheets API is primarily for document-oriented functionality as described above, but to perform file-level access such as uploads & downloads, imports & exports (same as uploads & downloads but conversion to/from various formats), use the Google Drive API instead. Examples of using the Drive API:
Exporting a Google Sheet as CSV (blog post only)
"Poor man's plain text to PDF" converter (blog post only) (*)
(*) - TL;DR: upload plain text file to Drive, import/convert to Google Docs format, then export that Doc as PDF. Post above uses Drive API v2; this follow-up post describes migrating it to Drive API v3, and here's a video combining both "poor man's converter" posts.

With the latest Sheet API you can programmatically add a conditional formatting rule to your sheet to do the highlighting.
You can add a custom formula rule that will set the background colour to green in column A where column O is "X" like this:
function applyConditionalFormatting() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var numRows = sheet.getLastRow();
var rangeToHighlight = sheet.getRange("A2:A" + numRows);
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenFormulaSatisfied('=INDIRECT("R[0]C[14]", FALSE)="X"')
.setBackground("green")
.setRanges([rangeToHighlight])
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);
}
The range that the conditional formatting applies to is the A column from row 2 to the last row in the sheet.
The custom formula is:
=INDIRECT("R[0]C[14]", FALSE)="X"
which means go 14 columns to the right of the selected range column and check if its value is "X".
Column O is 14 columns to the right of column A.

Related

Insert Image from Google Sheets cell into Google Slides [duplicate]

The new function Insert > Image > Image in Cell in Google sheets inserts an image in a cell and not as an OverGridImage.
I would like to insert the image in this manner and then access the image from Google Apps Script. Is this possible?
After inserting the image the formula of the cell is blank when the cell is selected. I tried searching the GAS reference, but I could not find any information on this relatively new feature.
There is information on the over grid images. I would expect the in-cell image to have similar functions.
I've tried things like this:
// See what information is available on a cell with inserted image:
var image = sheet.getRange(1, 1).getFormula();
Logger.log(image);
The logs shows up empty.
I tried several: .getImage() (does not exist), .getValue(), .getFormula()
I would expect to be able to access the image URL or Blob in some way.
Answer:
This is a new feature and unfortunately at current there isn’t a method to be able to get an image inserted into a Cell this way using Google Apps Script, nor using the Sheets API.
More Information:
Attempting to get the data in a cell using the spreadsheets.get method with the following parameters
spreadsheetId: "ID of private spreadsheet created in Drive"
includeGridData: True
ranges: D7
fields: sheets/data/rowData/values
Will return a 200 response, however the image data is not returned:
{
"sheets": [
{
"data": [
{
"rowData": [
{
"values": [
{
"userEnteredValue": {},
"effectiveValue": {},
"effectiveFormat": {
"backgroundColor": {
"red": 1,
"green": 1,
"blue": 1
},
"padding": {
"top": 2,
"right": 3,
"bottom": 2,
"left": 3
},
"horizontalAlignment": "LEFT",
"verticalAlignment": "BOTTOM",
"wrapStrategy": "OVERFLOW_CELL",
"textFormat": {
"foregroundColor": {},
"fontFamily": "Arial",
"fontSize": 10,
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false
},
"hyperlinkDisplayType": "PLAIN_TEXT"
}
}
]
}
]
}
]
}
]
}
Feature Request:
There is however a Feature request for this on Google’s Issue Tracker which you can find here. If you head over to the feature request page and click the star in the top left, you can let Google know that you also would like this feature, and will automatically get updates about its progress.
I believe your goal as follows.
You want to retrieve the image in the cell of Google Spreadsheet using Google Apps Script.
Issue and workaround:
Unfortunately, in the current stage, there are no methods for retrieving the images in the cell on Spreadsheet in Spreadsheet service and Sheets API. This has already been mentioned by Rafa Guillermo's answer. So in this answer, I would like to propose a workaround for retrieving the images in the cells using Google Apps Script.
In this workaround, I use Microsoft Excel Data converted from Google Spreadsheet. Even when Google Spreadsheet is converted to Microsoft Excel Data, the images in the cells are not removed. I use this. Of course, the images can be also retrieved from HTML data converted from Spreadsheet. But in this case, the parse of HTML data is a bit complicated than that of Excel data. So here, I would like to propose to retrieve the images from Excel Data converted from Spreadsheet. The flow of this workaround is as follows.
Convert Google Spreadsheet to Microsoft Excel (XLSX data) using Drive API.
Parse XLSX data using Google Apps Script.
When the converted XLSX data is unzipped, the data can be analyzed as the XML data. Fortunately, at Microsoft Docs, the detail specification is published as Open XML. So in this case, Microsoft Docs like XLSX, DOCX and PPTX can be analyzed using XmlService of Google Apps Script. I think that this method will be also useful for other situations.
Retrieve images from XLSX data.
Pattern 1:
In this pattern, I would like to introduce a simple method.
Sample script:
function myFunction() {
const spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
const url = "https://docs.google.com/spreadsheets/export?exportFormat=xlsx&id=" + spreadsheetId;
const blob = UrlFetchApp.fetch(url, {headers: {authorization: `Bearer ${ScriptApp.getOAuthToken()}`}}).getBlob().setContentType(MimeType.ZIP);
const xlsx = Utilities.unzip(blob);
xlsx.forEach(b => {
const name = b.getName().match(/xl\/media\/(.+)/);
if (name) DriveApp.createFile(b.setName(name[1]));
});
}
In this sample script, all images in the Spreadsheet are exported as the files. So in this case, both images in the cells and over the cells from all sheets in the Spreadsheet are retrieved. And also, it cannot retrieve the cell coordinate that the image is in the cell.
In the current stage, there are no methods for retrieving the images in Google Spreadsheet as the blob. In this sample script, this can be achieved.
This sample script cannot export the drawings. Please be careful this.
When setContentType(MimeType.ZIP) is not used, an error occurs at Utilities.unzip(blob). Please be careful this.
Pattern 2:
In this pattern, the images are retrieved with the sheet name and cell coordinate from Spreadsheet. In this case, the script becomes a bit complicated. So here, I would like to introduce the sample script using a Google Apps Script library. Ref Of course, you can see the whole script there.
Sample script:
Before you use this script, please install DocsServiceApp (The author of this GAS library is tanaike.) of the Google Apps Script library. Ref And run the function of myFunction.
function myFunction() {
const cell = "A1";
const sheetName = "Sheet1";
const spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
const obj = DocsServiceApp.openBySpreadsheetId(spreadsheetId).getSheetByName(sheetName).getImages();
console.log(obj)
const blobs = obj.filter(({range, image}) => range.a1Notation == cell && image.innerCell);
console.log(blobs.length)
if (blobs.length > 0) DriveApp.createFile(blobs[0].image.blob);
}
In this sample, the image in the cell "A1" of "Sheet1" in the active Spreadsheet is retrieved, and the retrieved blob is created to the root folder as an image file.
Note:
In the current stage, when an image is inserted to Google Spreadsheet and the Spreadsheet is converted to XLSX data, the image including the XLSX data has the filename of image1, image2,,, which are not the original filename. So it seems that this is the current specification.
When the images are retrieved from XLSX data, it seems that the image is a bit different from the original one. The image format is the same. But the data size is smaller than that of the original. When the image size is more than 2048 pixels and 72 dpi, the image is modified to 2048 pixels and 72 dpi. Even when the image size is less than 2048 pixels and 72 dpi, the file size becomes smaller than that of original one. So I think that the image might be compressed. Please be careful this.
In the current stage, the drawings cannot be directly retrieved.
References:
Understanding the Open XML file formats
XML Service
DocsServiceApp
Now available as of January 2022 (release notes):
The following classes have been added to the Spreadsheet Service to let you add images to cells:
CellImageBuilder: This builder creates the image value needed to add an image to a >cell.
CellImage: Represents an image to add to a cell.
To add an image to a cell, you must create a new image value for the image using SpreadsheetApp.newCellImage() and CellImageBuilder. Then, use Range.setValue(value) or Range.setValues(values) to add the image value to the cell.
Example:
function insertImageIntoCell()
{
let image = SpreadsheetApp.newCellImage().setSourceUrl('https://www.gstatic.com/images/branding/product/2x/apps_script_48dp.png').setAltTextDescription('Google Apps Script logo').toBuilder().build();
SpreadsheetApp.getActive().getActiveSheet().getRange('A1').setValue(image);
}
Result:
function getImageFromCell()
{
let value = SpreadsheetApp.getActive().getActiveSheet().getRange('A1').getValue();
console.log(value.getAltTextDescription());
console.log(value.getUrl());
}
Result:
Note: getUrl returns null for this particular example, which seems to be due some internal API unavailability, from docs:
Gets the image's source URL; returns null if the URL is unavailable. If the image was inserted by URL using an API, this method returns the URL provided during image insertion.
This answer is about INSERTING in-cell images. I haven't been able to find a way to actually extract image data so Panos's answer is the best option for reading in-cell image data.
There are a few different ways to do this, some of them use some undocumented APIs.
1. =IMAGE(<http url>)
The =IMAGE is a standard function which displays in image within a cell. It does almost the exact same thing as manually inserting an in-cell image.
2. Copied-by-value =IMAGE
Once you have an =IMAGE image you can copy it and paste it by-value which will duplicate the image without the formula (if you want that for some reason). You can do this in a script using the copyTo function:
srcImageRange.copyTo(dstRange, { contentsOnly: true })
This formula-less IMAGE is only distinguishable from a true in-cell image in that when you right-click on it is missing the "Alt text" and "Put image over cells" context menu options. Those options only show up on real in-cell images.
3. The undocumented CellImage APIs
When you call getValue() on a in-cell image (both formula and manually inserted) you get a CellImage instance.
CellImage
Prop/method
(Return) Type
Description
toString()
string
returns "CellImage".
getContentUrl()
?
always throws an error?
toBuilder()
CellImageBuilder
Convert this into an writable CellImageBuilder instance.
getAltTextDescription()
string
Returns the alt text description.
getAltTextTitle()
string
Returns the alt text title.
getUrl()
?
Doesn't seem to work, always returns undefined. :(
valueType
?
Same as SpreadsheetApp.ValueType, doesn't seem meaningful.
CellImageBuilder
Has all the same properties and methods as CellImage with these additional ones:
Prop/method
(Return) Type
Description
toString()
string
returns "CellImageBuilder".
build()
CellImage
Convert into a (read-only) CellImage instance.
setSourceUrl(string)
void
Update the image by supplying a web or data URL.
setAltTextTitle(string)
void
Sets the alt text title.
setAltTextDescription(string)
void
Sets the alt text description.
The major benefit I see with using this over IMAGE() is that it supports data URLs and therefore indirectly supports blobs.
Working Example Code
Keep in mind the undocumented APIs might change without notice.
Link to Example Spreadhseet
// 1 (or just use IMAGE in formula directly)
function insertImageFormula(range, httpUrl) {
range.setFormula(`=IMAGE("${httpUrl}")`);
}
// 2
function insertImageValue(range, httpUrl) {
range.setFormula(`=IMAGE("${httpUrl}")`);
SpreadsheetApp.flush(); // Flush needed for image to load.
range.copyTo(range, { contentsOnly: true }); // Copy value onto itself, removing the formula.
}
// 3
function insertCellImage(range, sourceUrl) {
range.setFormula('=IMAGE("http")'); // Set blank image to get CellImageBuilder handle.
const builder = range.getValue().toBuilder();
builder.setSourceUrl(sourceUrl);
builder.setAltTextDescription(sourceUrl); // Put url in description for later identification, for example.
range.setValue(builder.build());
}
const DATA_URI = "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7///"
+ "/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAos"
+ "J6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7";
function test() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
sheet.clear();
sheet.getRange(1, 1).setValue("IMAGE formula");
insertImageFormula(sheet.getRange(2, 1), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");
sheet.getRange(1, 2).setValue("Copied-by-value IMAGE");
insertImageValue(sheet.getRange(2, 2), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");
sheet.getRange(1, 3).setValue("In-Cell Image (Http URL)");
insertCellImage(sheet.getRange(2, 3), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");
sheet.getRange(1, 4).setValue("In-Cell Image (DATA URI)");
insertCellImage(sheet.getRange(2, 4), DATA_URI);
sheet.getRange(1, 5).setValue("In-Cell Image (Blob DATA URI)");
const blob = UrlFetchApp.fetch("https://www.gstatic.com/script/apps_script_1x_24dp.png").getBlob();
insertCellImage(sheet.getRange(2, 5), blobToDataUrl(blob));
}
function blobToDataUrl(blob) {
return `data:${blob.getContentType()};base64,${Utilities.base64Encode(blob.getBytes())}`
}
Both Rafa Guillermo and Tanaike requested that I make an answer based on my comment to Tanaike’s post. I do so below, but it falls into the category of a workaround rather than an "answer". A true answer would address the exact question in the original post.
As I said in my comment, I’ve used this method for simple cases, and I’ve also done some tests which suggest it preserves image resolution. Since I've only used this for simple cases like the one below, I don't know how generally it will work.
The steps I provide below are (to the best of my ability) what I remember going through as I did one specific example. Here are the first dozen rows of the final result after using this method:
This example had a total of 7100+ rows
Column 1 contained 430+ images or blank cells, most of which repeated
multiple times
Column 2 contained unique IDs for each image
Column 3 are the file names which were tied to each ID using the
method below
Steps to extract images from google sheet cells:
Resize column and rows containing images to something large (eg, 300)
Use File>Publish to Web & paste generated link into a new tab
In Chrome, use File>Save Page As…>Webpage, Complete
Images will be found in an html folder ending with _files
If needed, rename files to use image extension and list in order*
To key downloaded image file names to image cells in the sheet:
Duplicate sheet since the following will remove original data
Select columns containing images and IDs and use Data>Remove Duplicates
Add a new column next to the IDs containing the file names**
Use VLOOKUP function to transfer all file names to original sheet based on the unique IDs***
*In my example the images all had names like p.txt, p(1).txt, p(2).txt, etc… In Mac OS Finder, I selected all files and used right click>Rename files… and then the replace option to replace .txt with .jpg, (1) with (001), etc…
**file name listing can be obtained, for example, using the Terminal ls -l command
***for example, I used: =vlookup(B2,unique!$B$2:$C$430,2,false)
This question is a little old, but since I faced today this problem, please allow me to share my experience.
I realized that the getValue() of the cell, returns an object that its text is "CellImage". This allows me to understand that there is an embedded image in this cell. This objects seems to be similar to (or the same) with the OverGridImage object. At least, you can use the getAltTextTitle and the getAltTextDescription methods.
By combining all these features, my workaround is:
Add specific AltText to the image in the cell.
Get the value of the cell in an object.
Check if this is equals to "CellImage".
If it is CellImage, get the AltText.
Based on the value of this AltText do whatever you like.
The sample code follows:
/*-------------------------------------------------------------------------
Custom event handler triggered when a single cell is selected in the spreadsheet.
#param {Event} e The onSelectionChange event.
-------------------------------------------------------------------------*/
function onSingleCellSelected(e) {
var cell = e.range.getCell(1, 1);
var v = cell.getValue();
if(v == "CellImage") {
var altText = v.getAltTextTitle();
Logger.log(v.getAltTextDescription());
if(altText == "#action(recordTime)"){
cell.setBackground("cyan");
}
}
}
I just tried something pretty basic and it worked. Maybe doesn't work in all cases, depends if you added the images previously through a formula...
Add image through Google Apps Script :
var ss = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
var sheet = ss.getSheetByName(SHEET_NAME);
sheet.getRange('A1').setFormula('=IMAGE("https://developers.google.com/google-ads/scripts/images/reports.png")');
Worked (it's in the cell and will work auto fit on resizing) :
Then to retrieve the image url from cell :
var imgVal = sheet.getRange('A1').getFormula();
var regEx = /"(.*)"/gm;
var url = regEx.exec(imgVal)[1];
Logger.log(url);
Logs will be :

How to set a named range for a data validation programmatically (in Google apps script) in a Google spreadsheet?

Use Case
Example. I have a named range Apples (address "Sheet10!B2:B"), which in use for data validation for plenty of sheet cells. The data range for Apples can be changed (in a script), e.g. to "Sheet10!D2:D".
It works from UI
I can set manually a named range as a data source of data validation.
In this case, the data validation of a cell will always refer to the named range Apples with updated the data range.
How to make it in Google Apps Script?
GAS Limits
The code, for setting data validation, should look like this, if you have a namedRange object:
mySheet.getRange('F5')
.setDataValidation(
SpreadsheetApp.newDataValidation()
.requireValueInRange(
namedRange.getRange()
)
.setAllowInvalid(false)
.build()
);
DataValidationBuilder.requireValueInRange() does not work here as it requires only class Range (it cannot get NamedRange), and no reference to a named range will be used.
Is there a workaround or so?
UPD1 - Spreadsheet.getRangeByName() does not work
Getting range by name does not help, the data validation will get actual range address.
SpreadsheetApp.getActive().getRangeByName("Apples")
UPD2 No way to make it so far in GAS
As #TheMaster posted, it's not possible at this moment.
Please set +1 for posts:
https://issuetracker.google.com/issues/143913035
https://issuetracker.google.com/issues/203557342
P.S. It looks like the only solution will work is Google Sheets API.
I thought that in your situation, I thought that when Sheets API is used, your goal might be able to be used.
Workaround 1:
This workaround uses Sheets API.
Usage:
1. Prepare a Google Spreadsheet.
Please create a new Google Spreadsheet.
From Example. I have a named range Apples (address "Sheet10!B2:B"), which in use for data validation for plenty of sheet cells. The data range for Apples can be changed (in a script), e.g. to "Sheet10!D2:D"., please insert a sheet of "Sheet10" and put sample values to the cells "B2:B" and "D2:D".
Please set the named range Sheet10!B2:B as Apple.
2. Sample script.
Please copy and paste the following script to the script editor of Spreadsheet and save the script. And, please enable Sheets API at Advanced Google services.
function myFunction() {
const namedRangeName = "Apple"; // Please set the name of the named range.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Sheet10");
const requests = [{ updateCells: { range: { sheetId: sheet.getSheetId(), startRowIndex: 0, endRowIndex: 1, startColumnIndex: 0, endColumnIndex: 1 }, rows: [{ values: [{ dataValidation: { condition: { values: [{ userEnteredValue: "=" + namedRangeName }], type: "ONE_OF_RANGE" }, showCustomUi: true } }] }], fields: "dataValidation" } }];
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
}
In this request, the name of the named range is directly put to userEnteredValue.
3. Testing.
When this script is run to the above sample Spreadsheet, the following result is obtained.
When this demonstration is seen, first, you can see the named range of "Apple" which has the cells "B1:B1000". When a script is run, data validation is put to the cell "A1" with the named range of "Apple". In this case, the values of data validation indicate "B1:B1000". When the range named range "Apple" is changed from "B1:B1000" to "D1:D1000" and the data validation of "A1" is confirmed, it is found that the values are changed from "B1:B1000" to "D1:D1000".
Workaround 2:
This workaround uses the Google Spreadsheet service (SpreadsheetApp). In the current stage, it seems that the Google Spreadsheet service (SpreadsheetApp) cannot directly achieve your goal. This has already been mentioned in the discussions in the comment and TheMaster's answer. When you want to achieve this, how about checking whether the range of the named range is changed using OnChange as following workaround 2?
Usage:
1. Prepare a Google Spreadsheet.
Please create a new Google Spreadsheet.
From Example. I have a named range Apples (address "Sheet10!B2:B"), which in use for data validation for plenty of sheet cells. The data range for Apples can be changed (in a script), e.g. to "Sheet10!D2:D"., please insert a sheet of "Sheet10" and put sample values to the cells "B2:B" and "D2:D".
Please set the named range Sheet10!B2:B as Apple.
2. Sample script.
Please copy and paste the following script to the script editor of Spreadsheet and save the script. And, please install OnChange trigger to the function onChange.
First, please run createDataValidation. By this, data validation is put to the cell "A1" of "Sheet10". In this case, the set range is the range retrieved from the named range "Apple". So, in this case, the range is Sheet10!B2:B1000.
As the next step, please change the range of the named range from Sheet10!B2:B1000 to Sheet10!D2:D1000. By this, onChange` function is automatically run by the installed OnChange trigger. By this, the data validation of "A2" is updated. By this, the values of data validation are changed.
const namedRangeName = "Apple"; // Please set the name of the named range.
const datavalidationCell = "Sheet10!A2"; // As a sample. data validation is put to this cell.
function onChange(e) {
if (e.changeType != "OTHER") return;
const range = e.source.getRangeByName(namedRangeName);
const a1Notation = `'${range.getSheet().getSheetName()}'!${range.getA1Notation()}`;
const prop = PropertiesService.getScriptProperties();
const previousRange = prop.getProperty("previousRange");
if (previousRange != a1Notation) {
const rule = SpreadsheetApp.newDataValidation().requireValueInRange(e.source.getRangeByName(namedRangeName)).setAllowInvalid(false).build();
e.source.getRange(datavalidationCell).setDataValidation(rule);
}
prop.setProperty("previousRange", a1Notation);
}
// First, please run this function.
function createDataValidation() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const rule = SpreadsheetApp.newDataValidation().requireValueInRange(ss.getRangeByName(namedRangeName)).setAllowInvalid(false).build();
ss.getRange(datavalidationCell).setDataValidation(rule);
const prop = PropertiesService.getScriptProperties();
const range = ss.getRangeByName(namedRangeName);
const a1Notation = `'${range.getSheet().getSheetName()}'!${range.getA1Notation()}`;
prop.setProperty("previousRange", a1Notation);
}
References:
Method: spreadsheets.batchUpdate
UpdateCellsRequest
DataValidationRule
Currently, This seems to be impossible. This is however a known issue. +1 this feature request, if you want this implemented.
https://issuetracker.google.com/issues/143913035
Workarounds from the tracker issue creator:
If a validation rule is manually created with a NamedRange via the Sheets GUI, it can then be copied programmatically using Range.getDataValidations(), and subsequently used to programmatically create new DataValidations. DataValidations created this way maintain their connection to the NamedRange, and behave like their manually created counterparts. This demonstrates that the functionality to 'use' NamedRanges for data validation rules is already possible with Apps Scripts, but not the option to 'create' them.
As a half-answer, if you want just validation and can live without the drop-down list of valid values, you can programmatically set a custom formula that references the named range. This reference to the named range will not get expanded in the AppsScript, so future changes to the Named Range's actual range will percolate to the validator. Like so:
mySheet.getRange('F5')
.setDataValidation(
SpreadsheetApp.newDataValidation()
.requireFormulaSatisfied(
'=EQ(F5, VLOOKUP(F5, ' + namedRange.getName() + ', 1))'
)
.setAllowInvalid(false)
.build()
);
(The formula just checks that the value in the cell being tested is equal to what VLOOKUP finds for that cell, in the first column -- I'm assuming the named range content is sorted.)
Use getRangeByName()
function lfunko() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
var cell = sh.getRange(1, 10);//location where datavalidation is applied
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(ss.getRangeByName("MyList")).build();
cell.setDataValidation(rule);
}

Google Sheet Script: how to export single cells as images? [duplicate]

The new function Insert > Image > Image in Cell in Google sheets inserts an image in a cell and not as an OverGridImage.
I would like to insert the image in this manner and then access the image from Google Apps Script. Is this possible?
After inserting the image the formula of the cell is blank when the cell is selected. I tried searching the GAS reference, but I could not find any information on this relatively new feature.
There is information on the over grid images. I would expect the in-cell image to have similar functions.
I've tried things like this:
// See what information is available on a cell with inserted image:
var image = sheet.getRange(1, 1).getFormula();
Logger.log(image);
The logs shows up empty.
I tried several: .getImage() (does not exist), .getValue(), .getFormula()
I would expect to be able to access the image URL or Blob in some way.
Answer:
This is a new feature and unfortunately at current there isn’t a method to be able to get an image inserted into a Cell this way using Google Apps Script, nor using the Sheets API.
More Information:
Attempting to get the data in a cell using the spreadsheets.get method with the following parameters
spreadsheetId: "ID of private spreadsheet created in Drive"
includeGridData: True
ranges: D7
fields: sheets/data/rowData/values
Will return a 200 response, however the image data is not returned:
{
"sheets": [
{
"data": [
{
"rowData": [
{
"values": [
{
"userEnteredValue": {},
"effectiveValue": {},
"effectiveFormat": {
"backgroundColor": {
"red": 1,
"green": 1,
"blue": 1
},
"padding": {
"top": 2,
"right": 3,
"bottom": 2,
"left": 3
},
"horizontalAlignment": "LEFT",
"verticalAlignment": "BOTTOM",
"wrapStrategy": "OVERFLOW_CELL",
"textFormat": {
"foregroundColor": {},
"fontFamily": "Arial",
"fontSize": 10,
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false
},
"hyperlinkDisplayType": "PLAIN_TEXT"
}
}
]
}
]
}
]
}
]
}
Feature Request:
There is however a Feature request for this on Google’s Issue Tracker which you can find here. If you head over to the feature request page and click the star in the top left, you can let Google know that you also would like this feature, and will automatically get updates about its progress.
I believe your goal as follows.
You want to retrieve the image in the cell of Google Spreadsheet using Google Apps Script.
Issue and workaround:
Unfortunately, in the current stage, there are no methods for retrieving the images in the cell on Spreadsheet in Spreadsheet service and Sheets API. This has already been mentioned by Rafa Guillermo's answer. So in this answer, I would like to propose a workaround for retrieving the images in the cells using Google Apps Script.
In this workaround, I use Microsoft Excel Data converted from Google Spreadsheet. Even when Google Spreadsheet is converted to Microsoft Excel Data, the images in the cells are not removed. I use this. Of course, the images can be also retrieved from HTML data converted from Spreadsheet. But in this case, the parse of HTML data is a bit complicated than that of Excel data. So here, I would like to propose to retrieve the images from Excel Data converted from Spreadsheet. The flow of this workaround is as follows.
Convert Google Spreadsheet to Microsoft Excel (XLSX data) using Drive API.
Parse XLSX data using Google Apps Script.
When the converted XLSX data is unzipped, the data can be analyzed as the XML data. Fortunately, at Microsoft Docs, the detail specification is published as Open XML. So in this case, Microsoft Docs like XLSX, DOCX and PPTX can be analyzed using XmlService of Google Apps Script. I think that this method will be also useful for other situations.
Retrieve images from XLSX data.
Pattern 1:
In this pattern, I would like to introduce a simple method.
Sample script:
function myFunction() {
const spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
const url = "https://docs.google.com/spreadsheets/export?exportFormat=xlsx&id=" + spreadsheetId;
const blob = UrlFetchApp.fetch(url, {headers: {authorization: `Bearer ${ScriptApp.getOAuthToken()}`}}).getBlob().setContentType(MimeType.ZIP);
const xlsx = Utilities.unzip(blob);
xlsx.forEach(b => {
const name = b.getName().match(/xl\/media\/(.+)/);
if (name) DriveApp.createFile(b.setName(name[1]));
});
}
In this sample script, all images in the Spreadsheet are exported as the files. So in this case, both images in the cells and over the cells from all sheets in the Spreadsheet are retrieved. And also, it cannot retrieve the cell coordinate that the image is in the cell.
In the current stage, there are no methods for retrieving the images in Google Spreadsheet as the blob. In this sample script, this can be achieved.
This sample script cannot export the drawings. Please be careful this.
When setContentType(MimeType.ZIP) is not used, an error occurs at Utilities.unzip(blob). Please be careful this.
Pattern 2:
In this pattern, the images are retrieved with the sheet name and cell coordinate from Spreadsheet. In this case, the script becomes a bit complicated. So here, I would like to introduce the sample script using a Google Apps Script library. Ref Of course, you can see the whole script there.
Sample script:
Before you use this script, please install DocsServiceApp (The author of this GAS library is tanaike.) of the Google Apps Script library. Ref And run the function of myFunction.
function myFunction() {
const cell = "A1";
const sheetName = "Sheet1";
const spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
const obj = DocsServiceApp.openBySpreadsheetId(spreadsheetId).getSheetByName(sheetName).getImages();
console.log(obj)
const blobs = obj.filter(({range, image}) => range.a1Notation == cell && image.innerCell);
console.log(blobs.length)
if (blobs.length > 0) DriveApp.createFile(blobs[0].image.blob);
}
In this sample, the image in the cell "A1" of "Sheet1" in the active Spreadsheet is retrieved, and the retrieved blob is created to the root folder as an image file.
Note:
In the current stage, when an image is inserted to Google Spreadsheet and the Spreadsheet is converted to XLSX data, the image including the XLSX data has the filename of image1, image2,,, which are not the original filename. So it seems that this is the current specification.
When the images are retrieved from XLSX data, it seems that the image is a bit different from the original one. The image format is the same. But the data size is smaller than that of the original. When the image size is more than 2048 pixels and 72 dpi, the image is modified to 2048 pixels and 72 dpi. Even when the image size is less than 2048 pixels and 72 dpi, the file size becomes smaller than that of original one. So I think that the image might be compressed. Please be careful this.
In the current stage, the drawings cannot be directly retrieved.
References:
Understanding the Open XML file formats
XML Service
DocsServiceApp
Now available as of January 2022 (release notes):
The following classes have been added to the Spreadsheet Service to let you add images to cells:
CellImageBuilder: This builder creates the image value needed to add an image to a >cell.
CellImage: Represents an image to add to a cell.
To add an image to a cell, you must create a new image value for the image using SpreadsheetApp.newCellImage() and CellImageBuilder. Then, use Range.setValue(value) or Range.setValues(values) to add the image value to the cell.
Example:
function insertImageIntoCell()
{
let image = SpreadsheetApp.newCellImage().setSourceUrl('https://www.gstatic.com/images/branding/product/2x/apps_script_48dp.png').setAltTextDescription('Google Apps Script logo').toBuilder().build();
SpreadsheetApp.getActive().getActiveSheet().getRange('A1').setValue(image);
}
Result:
function getImageFromCell()
{
let value = SpreadsheetApp.getActive().getActiveSheet().getRange('A1').getValue();
console.log(value.getAltTextDescription());
console.log(value.getUrl());
}
Result:
Note: getUrl returns null for this particular example, which seems to be due some internal API unavailability, from docs:
Gets the image's source URL; returns null if the URL is unavailable. If the image was inserted by URL using an API, this method returns the URL provided during image insertion.
This answer is about INSERTING in-cell images. I haven't been able to find a way to actually extract image data so Panos's answer is the best option for reading in-cell image data.
There are a few different ways to do this, some of them use some undocumented APIs.
1. =IMAGE(<http url>)
The =IMAGE is a standard function which displays in image within a cell. It does almost the exact same thing as manually inserting an in-cell image.
2. Copied-by-value =IMAGE
Once you have an =IMAGE image you can copy it and paste it by-value which will duplicate the image without the formula (if you want that for some reason). You can do this in a script using the copyTo function:
srcImageRange.copyTo(dstRange, { contentsOnly: true })
This formula-less IMAGE is only distinguishable from a true in-cell image in that when you right-click on it is missing the "Alt text" and "Put image over cells" context menu options. Those options only show up on real in-cell images.
3. The undocumented CellImage APIs
When you call getValue() on a in-cell image (both formula and manually inserted) you get a CellImage instance.
CellImage
Prop/method
(Return) Type
Description
toString()
string
returns "CellImage".
getContentUrl()
?
always throws an error?
toBuilder()
CellImageBuilder
Convert this into an writable CellImageBuilder instance.
getAltTextDescription()
string
Returns the alt text description.
getAltTextTitle()
string
Returns the alt text title.
getUrl()
?
Doesn't seem to work, always returns undefined. :(
valueType
?
Same as SpreadsheetApp.ValueType, doesn't seem meaningful.
CellImageBuilder
Has all the same properties and methods as CellImage with these additional ones:
Prop/method
(Return) Type
Description
toString()
string
returns "CellImageBuilder".
build()
CellImage
Convert into a (read-only) CellImage instance.
setSourceUrl(string)
void
Update the image by supplying a web or data URL.
setAltTextTitle(string)
void
Sets the alt text title.
setAltTextDescription(string)
void
Sets the alt text description.
The major benefit I see with using this over IMAGE() is that it supports data URLs and therefore indirectly supports blobs.
Working Example Code
Keep in mind the undocumented APIs might change without notice.
Link to Example Spreadhseet
// 1 (or just use IMAGE in formula directly)
function insertImageFormula(range, httpUrl) {
range.setFormula(`=IMAGE("${httpUrl}")`);
}
// 2
function insertImageValue(range, httpUrl) {
range.setFormula(`=IMAGE("${httpUrl}")`);
SpreadsheetApp.flush(); // Flush needed for image to load.
range.copyTo(range, { contentsOnly: true }); // Copy value onto itself, removing the formula.
}
// 3
function insertCellImage(range, sourceUrl) {
range.setFormula('=IMAGE("http")'); // Set blank image to get CellImageBuilder handle.
const builder = range.getValue().toBuilder();
builder.setSourceUrl(sourceUrl);
builder.setAltTextDescription(sourceUrl); // Put url in description for later identification, for example.
range.setValue(builder.build());
}
const DATA_URI = "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7///"
+ "/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAos"
+ "J6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7";
function test() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
sheet.clear();
sheet.getRange(1, 1).setValue("IMAGE formula");
insertImageFormula(sheet.getRange(2, 1), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");
sheet.getRange(1, 2).setValue("Copied-by-value IMAGE");
insertImageValue(sheet.getRange(2, 2), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");
sheet.getRange(1, 3).setValue("In-Cell Image (Http URL)");
insertCellImage(sheet.getRange(2, 3), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");
sheet.getRange(1, 4).setValue("In-Cell Image (DATA URI)");
insertCellImage(sheet.getRange(2, 4), DATA_URI);
sheet.getRange(1, 5).setValue("In-Cell Image (Blob DATA URI)");
const blob = UrlFetchApp.fetch("https://www.gstatic.com/script/apps_script_1x_24dp.png").getBlob();
insertCellImage(sheet.getRange(2, 5), blobToDataUrl(blob));
}
function blobToDataUrl(blob) {
return `data:${blob.getContentType()};base64,${Utilities.base64Encode(blob.getBytes())}`
}
Both Rafa Guillermo and Tanaike requested that I make an answer based on my comment to Tanaike’s post. I do so below, but it falls into the category of a workaround rather than an "answer". A true answer would address the exact question in the original post.
As I said in my comment, I’ve used this method for simple cases, and I’ve also done some tests which suggest it preserves image resolution. Since I've only used this for simple cases like the one below, I don't know how generally it will work.
The steps I provide below are (to the best of my ability) what I remember going through as I did one specific example. Here are the first dozen rows of the final result after using this method:
This example had a total of 7100+ rows
Column 1 contained 430+ images or blank cells, most of which repeated
multiple times
Column 2 contained unique IDs for each image
Column 3 are the file names which were tied to each ID using the
method below
Steps to extract images from google sheet cells:
Resize column and rows containing images to something large (eg, 300)
Use File>Publish to Web & paste generated link into a new tab
In Chrome, use File>Save Page As…>Webpage, Complete
Images will be found in an html folder ending with _files
If needed, rename files to use image extension and list in order*
To key downloaded image file names to image cells in the sheet:
Duplicate sheet since the following will remove original data
Select columns containing images and IDs and use Data>Remove Duplicates
Add a new column next to the IDs containing the file names**
Use VLOOKUP function to transfer all file names to original sheet based on the unique IDs***
*In my example the images all had names like p.txt, p(1).txt, p(2).txt, etc… In Mac OS Finder, I selected all files and used right click>Rename files… and then the replace option to replace .txt with .jpg, (1) with (001), etc…
**file name listing can be obtained, for example, using the Terminal ls -l command
***for example, I used: =vlookup(B2,unique!$B$2:$C$430,2,false)
This question is a little old, but since I faced today this problem, please allow me to share my experience.
I realized that the getValue() of the cell, returns an object that its text is "CellImage". This allows me to understand that there is an embedded image in this cell. This objects seems to be similar to (or the same) with the OverGridImage object. At least, you can use the getAltTextTitle and the getAltTextDescription methods.
By combining all these features, my workaround is:
Add specific AltText to the image in the cell.
Get the value of the cell in an object.
Check if this is equals to "CellImage".
If it is CellImage, get the AltText.
Based on the value of this AltText do whatever you like.
The sample code follows:
/*-------------------------------------------------------------------------
Custom event handler triggered when a single cell is selected in the spreadsheet.
#param {Event} e The onSelectionChange event.
-------------------------------------------------------------------------*/
function onSingleCellSelected(e) {
var cell = e.range.getCell(1, 1);
var v = cell.getValue();
if(v == "CellImage") {
var altText = v.getAltTextTitle();
Logger.log(v.getAltTextDescription());
if(altText == "#action(recordTime)"){
cell.setBackground("cyan");
}
}
}
I just tried something pretty basic and it worked. Maybe doesn't work in all cases, depends if you added the images previously through a formula...
Add image through Google Apps Script :
var ss = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
var sheet = ss.getSheetByName(SHEET_NAME);
sheet.getRange('A1').setFormula('=IMAGE("https://developers.google.com/google-ads/scripts/images/reports.png")');
Worked (it's in the cell and will work auto fit on resizing) :
Then to retrieve the image url from cell :
var imgVal = sheet.getRange('A1').getFormula();
var regEx = /"(.*)"/gm;
var url = regEx.exec(imgVal)[1];
Logger.log(url);
Logs will be :

How to access new 'in-cell-image' from google apps script?

The new function Insert > Image > Image in Cell in Google sheets inserts an image in a cell and not as an OverGridImage.
I would like to insert the image in this manner and then access the image from Google Apps Script. Is this possible?
After inserting the image the formula of the cell is blank when the cell is selected. I tried searching the GAS reference, but I could not find any information on this relatively new feature.
There is information on the over grid images. I would expect the in-cell image to have similar functions.
I've tried things like this:
// See what information is available on a cell with inserted image:
var image = sheet.getRange(1, 1).getFormula();
Logger.log(image);
The logs shows up empty.
I tried several: .getImage() (does not exist), .getValue(), .getFormula()
I would expect to be able to access the image URL or Blob in some way.
Answer:
This is a new feature and unfortunately at current there isn’t a method to be able to get an image inserted into a Cell this way using Google Apps Script, nor using the Sheets API.
More Information:
Attempting to get the data in a cell using the spreadsheets.get method with the following parameters
spreadsheetId: "ID of private spreadsheet created in Drive"
includeGridData: True
ranges: D7
fields: sheets/data/rowData/values
Will return a 200 response, however the image data is not returned:
{
"sheets": [
{
"data": [
{
"rowData": [
{
"values": [
{
"userEnteredValue": {},
"effectiveValue": {},
"effectiveFormat": {
"backgroundColor": {
"red": 1,
"green": 1,
"blue": 1
},
"padding": {
"top": 2,
"right": 3,
"bottom": 2,
"left": 3
},
"horizontalAlignment": "LEFT",
"verticalAlignment": "BOTTOM",
"wrapStrategy": "OVERFLOW_CELL",
"textFormat": {
"foregroundColor": {},
"fontFamily": "Arial",
"fontSize": 10,
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false
},
"hyperlinkDisplayType": "PLAIN_TEXT"
}
}
]
}
]
}
]
}
]
}
Feature Request:
There is however a Feature request for this on Google’s Issue Tracker which you can find here. If you head over to the feature request page and click the star in the top left, you can let Google know that you also would like this feature, and will automatically get updates about its progress.
I believe your goal as follows.
You want to retrieve the image in the cell of Google Spreadsheet using Google Apps Script.
Issue and workaround:
Unfortunately, in the current stage, there are no methods for retrieving the images in the cell on Spreadsheet in Spreadsheet service and Sheets API. This has already been mentioned by Rafa Guillermo's answer. So in this answer, I would like to propose a workaround for retrieving the images in the cells using Google Apps Script.
In this workaround, I use Microsoft Excel Data converted from Google Spreadsheet. Even when Google Spreadsheet is converted to Microsoft Excel Data, the images in the cells are not removed. I use this. Of course, the images can be also retrieved from HTML data converted from Spreadsheet. But in this case, the parse of HTML data is a bit complicated than that of Excel data. So here, I would like to propose to retrieve the images from Excel Data converted from Spreadsheet. The flow of this workaround is as follows.
Convert Google Spreadsheet to Microsoft Excel (XLSX data) using Drive API.
Parse XLSX data using Google Apps Script.
When the converted XLSX data is unzipped, the data can be analyzed as the XML data. Fortunately, at Microsoft Docs, the detail specification is published as Open XML. So in this case, Microsoft Docs like XLSX, DOCX and PPTX can be analyzed using XmlService of Google Apps Script. I think that this method will be also useful for other situations.
Retrieve images from XLSX data.
Pattern 1:
In this pattern, I would like to introduce a simple method.
Sample script:
function myFunction() {
const spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
const url = "https://docs.google.com/spreadsheets/export?exportFormat=xlsx&id=" + spreadsheetId;
const blob = UrlFetchApp.fetch(url, {headers: {authorization: `Bearer ${ScriptApp.getOAuthToken()}`}}).getBlob().setContentType(MimeType.ZIP);
const xlsx = Utilities.unzip(blob);
xlsx.forEach(b => {
const name = b.getName().match(/xl\/media\/(.+)/);
if (name) DriveApp.createFile(b.setName(name[1]));
});
}
In this sample script, all images in the Spreadsheet are exported as the files. So in this case, both images in the cells and over the cells from all sheets in the Spreadsheet are retrieved. And also, it cannot retrieve the cell coordinate that the image is in the cell.
In the current stage, there are no methods for retrieving the images in Google Spreadsheet as the blob. In this sample script, this can be achieved.
This sample script cannot export the drawings. Please be careful this.
When setContentType(MimeType.ZIP) is not used, an error occurs at Utilities.unzip(blob). Please be careful this.
Pattern 2:
In this pattern, the images are retrieved with the sheet name and cell coordinate from Spreadsheet. In this case, the script becomes a bit complicated. So here, I would like to introduce the sample script using a Google Apps Script library. Ref Of course, you can see the whole script there.
Sample script:
Before you use this script, please install DocsServiceApp (The author of this GAS library is tanaike.) of the Google Apps Script library. Ref And run the function of myFunction.
function myFunction() {
const cell = "A1";
const sheetName = "Sheet1";
const spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
const obj = DocsServiceApp.openBySpreadsheetId(spreadsheetId).getSheetByName(sheetName).getImages();
console.log(obj)
const blobs = obj.filter(({range, image}) => range.a1Notation == cell && image.innerCell);
console.log(blobs.length)
if (blobs.length > 0) DriveApp.createFile(blobs[0].image.blob);
}
In this sample, the image in the cell "A1" of "Sheet1" in the active Spreadsheet is retrieved, and the retrieved blob is created to the root folder as an image file.
Note:
In the current stage, when an image is inserted to Google Spreadsheet and the Spreadsheet is converted to XLSX data, the image including the XLSX data has the filename of image1, image2,,, which are not the original filename. So it seems that this is the current specification.
When the images are retrieved from XLSX data, it seems that the image is a bit different from the original one. The image format is the same. But the data size is smaller than that of the original. When the image size is more than 2048 pixels and 72 dpi, the image is modified to 2048 pixels and 72 dpi. Even when the image size is less than 2048 pixels and 72 dpi, the file size becomes smaller than that of original one. So I think that the image might be compressed. Please be careful this.
In the current stage, the drawings cannot be directly retrieved.
References:
Understanding the Open XML file formats
XML Service
DocsServiceApp
Now available as of January 2022 (release notes):
The following classes have been added to the Spreadsheet Service to let you add images to cells:
CellImageBuilder: This builder creates the image value needed to add an image to a >cell.
CellImage: Represents an image to add to a cell.
To add an image to a cell, you must create a new image value for the image using SpreadsheetApp.newCellImage() and CellImageBuilder. Then, use Range.setValue(value) or Range.setValues(values) to add the image value to the cell.
Example:
function insertImageIntoCell()
{
let image = SpreadsheetApp.newCellImage().setSourceUrl('https://www.gstatic.com/images/branding/product/2x/apps_script_48dp.png').setAltTextDescription('Google Apps Script logo').toBuilder().build();
SpreadsheetApp.getActive().getActiveSheet().getRange('A1').setValue(image);
}
Result:
function getImageFromCell()
{
let value = SpreadsheetApp.getActive().getActiveSheet().getRange('A1').getValue();
console.log(value.getAltTextDescription());
console.log(value.getUrl());
}
Result:
Note: getUrl returns null for this particular example, which seems to be due some internal API unavailability, from docs:
Gets the image's source URL; returns null if the URL is unavailable. If the image was inserted by URL using an API, this method returns the URL provided during image insertion.
This answer is about INSERTING in-cell images. I haven't been able to find a way to actually extract image data so Panos's answer is the best option for reading in-cell image data.
There are a few different ways to do this, some of them use some undocumented APIs.
1. =IMAGE(<http url>)
The =IMAGE is a standard function which displays in image within a cell. It does almost the exact same thing as manually inserting an in-cell image.
2. Copied-by-value =IMAGE
Once you have an =IMAGE image you can copy it and paste it by-value which will duplicate the image without the formula (if you want that for some reason). You can do this in a script using the copyTo function:
srcImageRange.copyTo(dstRange, { contentsOnly: true })
This formula-less IMAGE is only distinguishable from a true in-cell image in that when you right-click on it is missing the "Alt text" and "Put image over cells" context menu options. Those options only show up on real in-cell images.
3. The undocumented CellImage APIs
When you call getValue() on a in-cell image (both formula and manually inserted) you get a CellImage instance.
CellImage
Prop/method
(Return) Type
Description
toString()
string
returns "CellImage".
getContentUrl()
?
always throws an error?
toBuilder()
CellImageBuilder
Convert this into an writable CellImageBuilder instance.
getAltTextDescription()
string
Returns the alt text description.
getAltTextTitle()
string
Returns the alt text title.
getUrl()
?
Doesn't seem to work, always returns undefined. :(
valueType
?
Same as SpreadsheetApp.ValueType, doesn't seem meaningful.
CellImageBuilder
Has all the same properties and methods as CellImage with these additional ones:
Prop/method
(Return) Type
Description
toString()
string
returns "CellImageBuilder".
build()
CellImage
Convert into a (read-only) CellImage instance.
setSourceUrl(string)
void
Update the image by supplying a web or data URL.
setAltTextTitle(string)
void
Sets the alt text title.
setAltTextDescription(string)
void
Sets the alt text description.
The major benefit I see with using this over IMAGE() is that it supports data URLs and therefore indirectly supports blobs.
Working Example Code
Keep in mind the undocumented APIs might change without notice.
Link to Example Spreadhseet
// 1 (or just use IMAGE in formula directly)
function insertImageFormula(range, httpUrl) {
range.setFormula(`=IMAGE("${httpUrl}")`);
}
// 2
function insertImageValue(range, httpUrl) {
range.setFormula(`=IMAGE("${httpUrl}")`);
SpreadsheetApp.flush(); // Flush needed for image to load.
range.copyTo(range, { contentsOnly: true }); // Copy value onto itself, removing the formula.
}
// 3
function insertCellImage(range, sourceUrl) {
range.setFormula('=IMAGE("http")'); // Set blank image to get CellImageBuilder handle.
const builder = range.getValue().toBuilder();
builder.setSourceUrl(sourceUrl);
builder.setAltTextDescription(sourceUrl); // Put url in description for later identification, for example.
range.setValue(builder.build());
}
const DATA_URI = "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7///"
+ "/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAos"
+ "J6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7";
function test() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
sheet.clear();
sheet.getRange(1, 1).setValue("IMAGE formula");
insertImageFormula(sheet.getRange(2, 1), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");
sheet.getRange(1, 2).setValue("Copied-by-value IMAGE");
insertImageValue(sheet.getRange(2, 2), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");
sheet.getRange(1, 3).setValue("In-Cell Image (Http URL)");
insertCellImage(sheet.getRange(2, 3), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");
sheet.getRange(1, 4).setValue("In-Cell Image (DATA URI)");
insertCellImage(sheet.getRange(2, 4), DATA_URI);
sheet.getRange(1, 5).setValue("In-Cell Image (Blob DATA URI)");
const blob = UrlFetchApp.fetch("https://www.gstatic.com/script/apps_script_1x_24dp.png").getBlob();
insertCellImage(sheet.getRange(2, 5), blobToDataUrl(blob));
}
function blobToDataUrl(blob) {
return `data:${blob.getContentType()};base64,${Utilities.base64Encode(blob.getBytes())}`
}
Both Rafa Guillermo and Tanaike requested that I make an answer based on my comment to Tanaike’s post. I do so below, but it falls into the category of a workaround rather than an "answer". A true answer would address the exact question in the original post.
As I said in my comment, I’ve used this method for simple cases, and I’ve also done some tests which suggest it preserves image resolution. Since I've only used this for simple cases like the one below, I don't know how generally it will work.
The steps I provide below are (to the best of my ability) what I remember going through as I did one specific example. Here are the first dozen rows of the final result after using this method:
This example had a total of 7100+ rows
Column 1 contained 430+ images or blank cells, most of which repeated
multiple times
Column 2 contained unique IDs for each image
Column 3 are the file names which were tied to each ID using the
method below
Steps to extract images from google sheet cells:
Resize column and rows containing images to something large (eg, 300)
Use File>Publish to Web & paste generated link into a new tab
In Chrome, use File>Save Page As…>Webpage, Complete
Images will be found in an html folder ending with _files
If needed, rename files to use image extension and list in order*
To key downloaded image file names to image cells in the sheet:
Duplicate sheet since the following will remove original data
Select columns containing images and IDs and use Data>Remove Duplicates
Add a new column next to the IDs containing the file names**
Use VLOOKUP function to transfer all file names to original sheet based on the unique IDs***
*In my example the images all had names like p.txt, p(1).txt, p(2).txt, etc… In Mac OS Finder, I selected all files and used right click>Rename files… and then the replace option to replace .txt with .jpg, (1) with (001), etc…
**file name listing can be obtained, for example, using the Terminal ls -l command
***for example, I used: =vlookup(B2,unique!$B$2:$C$430,2,false)
This question is a little old, but since I faced today this problem, please allow me to share my experience.
I realized that the getValue() of the cell, returns an object that its text is "CellImage". This allows me to understand that there is an embedded image in this cell. This objects seems to be similar to (or the same) with the OverGridImage object. At least, you can use the getAltTextTitle and the getAltTextDescription methods.
By combining all these features, my workaround is:
Add specific AltText to the image in the cell.
Get the value of the cell in an object.
Check if this is equals to "CellImage".
If it is CellImage, get the AltText.
Based on the value of this AltText do whatever you like.
The sample code follows:
/*-------------------------------------------------------------------------
Custom event handler triggered when a single cell is selected in the spreadsheet.
#param {Event} e The onSelectionChange event.
-------------------------------------------------------------------------*/
function onSingleCellSelected(e) {
var cell = e.range.getCell(1, 1);
var v = cell.getValue();
if(v == "CellImage") {
var altText = v.getAltTextTitle();
Logger.log(v.getAltTextDescription());
if(altText == "#action(recordTime)"){
cell.setBackground("cyan");
}
}
}
I just tried something pretty basic and it worked. Maybe doesn't work in all cases, depends if you added the images previously through a formula...
Add image through Google Apps Script :
var ss = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
var sheet = ss.getSheetByName(SHEET_NAME);
sheet.getRange('A1').setFormula('=IMAGE("https://developers.google.com/google-ads/scripts/images/reports.png")');
Worked (it's in the cell and will work auto fit on resizing) :
Then to retrieve the image url from cell :
var imgVal = sheet.getRange('A1').getFormula();
var regEx = /"(.*)"/gm;
var url = regEx.exec(imgVal)[1];
Logger.log(url);
Logs will be :

How can I set a filter view in a sheets script?

I have defined several filter views and would like to apply one of those predefined filter views inside of a Google sheets script.
NOTE: Filter Views are buried in menus and not all sheet users will know where to find them. Conversely, users making changes to filters and sorting change the view for all users unless Filter Views are used. Selecting Filter Views by prominent buttons solves these issues well for users of all skill levels.
This is a long standing feature request. However, there is no code here in your question to review.
https://code.google.com/p/google-apps-script-issues/issues/detail?id=524
Update
Three additional services have been added to Apps Script related to filters:
Class Filter and Range.createFilter, Sheet.getFilter.
These will not allow you to access or control the saved filter views, you might be able to script functionality similar. However, your options for UI buttons would either be image script runners or instantiate a side-bar app. Either of which might not be visible. Equally they affect the sheet for all viewers.
Further Update
As noted in an answer to a newer question, FilterViews can now be created programmatically. There are caveats noted in that answer that concern protected ranges.
This still doesn't address the OP request to activate specific FilterViews. They can be created, updated or deleted but even using the Sheets Advanced API, they cannot be activated programmatically by the current viewer.
In below example, a simple criteria TRUE/FALSE is used to filter sheet's view, i.e. if the value in cell is FALSE, the row is hidden.
Thus you have two functions, setFilter and clearFilter, apparently.
In the most primitive way, this would look something like this:
function setFilter() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var filterSettings = {};
// The range of data on which you want to apply the filter.
// optional arguments: startRowIndex, startColumnIndex, endRowIndex, endColumnIndex
filterSettings.range = {
sheetId: ss.getSheetByName("sheetName").getSheetId() // provide your sheetname to which you want to apply filter.
};
// Criteria for showing/hiding rows in a filter
// https://developers.google.com/sheets/api/reference/rest/v4/FilterCriteria
filterSettings.criteria = {};
var columnIndex = 9; // column that defines criteria [A = 0]
filterSettings['criteria'][columnIndex] = {
'hiddenValues': ["FALSE"]
};
var request = {
"setBasicFilter": {
"filter": filterSettings
}
};
Sheets.Spreadsheets.batchUpdate({'requests': [request]}, ss.getId());
}
function clearFilter() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssId = ss.getId();
var sheetId = ss.getSheetByName("sheetName").getSheetId();
var requests = [{
"clearBasicFilter": {
"sheetId": sheetId
}
}];
Sheets.Spreadsheets.batchUpdate({'requests': requests}, ssId);
}
What you want is to make it easy for your users to change filtered views.
An easy way achieve that is to use regular hyperlinks in frozen rows or columns:
This works because filtered views are activated just by an additional URL parameter named fvid:
Pros:
works across sheets and tabs.
easy to implement.
no coding required.
Cons:
filtered views need to be manually updated when rows exceed the range set in them because they do not accept open ranges...
it'not possible to use images as they do not accept hyperlinks
you need to create an "All rows" filtered view as removing the fvid parameter does not live update like adding or changing it.