Copy Document Title Using Google App Script - google-apps-script

I want to copy the text from the title within this document, but I am only used to using Google App Script in sheets, not docs:
I have tried lot of different methods, but nothing is populating in Logger.log(), so I am not sure it is working. Currently, all I want to return is the text in the Title, so I can go on to search for this in a spreadsheet. So far I have the following:
function autoFill() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var searchResult = body.findElement(DocumentApp.ElementType.PARAGRAPH);
var searchHeading = searchResult.getElement(DocumentApp.ParagraphHeading.TITLE);
The only part of the document that has the format of Title is the first line in the document.

Here is an example of how to find the paragraph with the Heading style Title. In my Doc I have a line of text "This is some text" with style Title.
function findTitle() {
try {
let doc = DocumentApp.getActiveDocument();
let body = doc.getBody();
let paras = body.getParagraphs();
let text = null;
paras.some( para => {
let attribs = para.getAttributes();
if( attribs["HEADING"] === DocumentApp.ParagraphHeading.TITLE ) {
text = para.getText();
return true;
}
return false;
}
);
console.log(text);
}
catch(err) {
console.log(err);
}
}
Execution log
8:08:39 AM Notice Execution started
8:08:39 AM Info This is some text
8:08:39 AM Notice Execution completed

Found that using Regular Expression would be able to identify the text needed:
function autoFill() {
var ss = DocumentApp.getActiveDocument();
var body = ss.getBody();
var header = DocumentApp.getActiveDocument().getHeader();
var contents = body.getText();
var racm = SpreadsheetApp.openById('[enter sheet ID').getSheetByName('Risks & Controls matrix');
var lr = racm.getLastRow();
var colID = racm.getRange(1,1,lr,1).getValues();
//you need to tweak below to the control name pattern
var regexName = /\w+[-]\w+[-]\w+/ //use website: regexr.com to create & www.geeksforgeeks.org/write-regular-expressions/
var titleFound=regexName.exec(contents)[0];
let row = colID.findIndex(users => {return users[0] == titleFound})+1;
Then goes on to finding the relevant data in the ranges from the spreadsheet and .replaceText() within the doc.
Works really well. Thanks for your help!

Related

How do I get the image ID of an image IN THE CELL?

I'm currently working on a code in Google Apps Script that allows a user to fill out a spreadsheet and have the spreadsheet generate printouts for a job board. I'm trying to design this in a way where the user can simply insert a logo image into a row of my Google sheet and have it replace a placeholder in my doc template.
I have found lots of answers about how you can take an image and convert it to a blob and insert it from a url or an ID, however, I can't seem to find a way to get the ID or url from the image in the cell.
Here's my code currently:
//Creates menu option on spreadsheet
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('AutoFill Docs');
menu.addItem('Create New Docs', 'createNewGoogleDocs');
menu.addToUi();
}
//Defines where to get template and info from
function createNewGoogleDocs() {
const googleDocTemplate = DriveApp.getFileById('14MJNd37pn6D-EmNKCQzXXvxJCcOAoB3KS-TlDgZuWMI');
const destinationFolder = DriveApp.getFolderById('120Sb_CJJlmz5NzJW8W3DB4TNuC4kdD3e');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('JobBoard');
const rows = sheet.getDataRange().getValues();
rows.forEach(function(row, index) {
if (index === 0) return;
if (row[9]) return;
const copy = googleDocTemplate.makeCopy(`${row[1]}, ${row[0]} Printout`, destinationFolder);
const doc = DocumentApp.openById(copy.getId())
const body = doc.getBody();
const friendlyDate = new Date(row[2]).toLocaleDateString();
//Replacing text
body.replaceText('{{Company}}', row[1]);
body.replaceText('{{jobTitle}}', row[0]);
body.replaceText('{{datePosted}}', friendlyDate);
body.replaceText('{{Description}}', row[3]);
body.replaceText('{{Qualifications}}', row[5]);
body.replaceText('{{Wage}}', row[4]);
body.replaceText('{{Apply}}', row[6]);
//A subfunction to handle replacing the image
function textToImage() {
var replaceTextToImage = function(body, searchText, image, width) {
var next = body.findText(searchText);
if (!next) return;
var r = next.getElement();
r.asText().setText("");
var img = r.getParent().asParagraph().insertInlineImage(0, image);
if (width && typeof width == 100) {
var w = img.getWidth();
var h = img.getHeight();
img.setWidth(width);
img.setHeight(width * h / w);
}
return next;
};
var documentId = doc;
var replaceText = "{{Upload Image}}";
var imageFileId = "### File ID of image ###"; //I don't know how to get this variable
var body = DocumentApp.openById(documentId).getBody();
var image = DriveApp.getFileById(imageFileId).getBlob();
do {
var next = replaceTextToImage(body, replaceText, image, 200);
} while (next);
}
//Close and saves new doc
doc.saveAndClose();
const url = doc.getUrl();
sheet.getRange(index + 1, 10).setValue(url)
})
}
I think what might be messing me up is that I have to loop through all my cells right now so that I can create multiple documents at once (meaning each row will have a different doc and different image ID). I'm just not sure how to work around that.
Here's the template and spreadsheet
https://docs.google.com/spreadsheets/d/1cySHogAxcUgzr0hsJoTyPZakKQkM6uIOtmyPzcMoJUM/edit?usp=sharing
https://docs.google.com/document/d/14MJNd37pn6D-EmNKCQzXXvxJCcOAoB3KS-TlDgZuWMI/edit?usp=sharing
There is a bit of an issue trying to get an image in a specific cell. There's even a Feature Request for that. This year Google released a few classes for image management but there seems to be issues when retrieving those using cellImage class.
I found a related answer (workaround) from user #Tanaike where images are retrieved from Google Sheets, converted to a Blob and inserted into a Google Doc.
Sample code provided was:
const spreadsheetId = "###"; // Google Spreadsheet ID
const res = DocsServiceApp.openBySpreadsheetId(spreadsheetId).getSheetByName("Sheet1").getImages();
console.log(res); // You can check the retrieved images at the log.
if (res.length == 0) return;
const blob = res[0].image.blob; // Here, 1st image of Sheet1 is retrieved. Of course, you can choose the image on the sheet.
let doc = DocumentApp.create("newDocName Goes_Here");
var body = doc.getBody();
var imgPDF = body.appendImage(blob);
Take into consideration that to make the above work you need to:
Install Google Apps Script library. (instructions here)
Enable Drive API.
I tested this and indeed, got the images from the given sheet and inserted them into the Google Doc specified. For some reason, running your code did not show me a newly created file from the template but you can tweak the above accordingly to your case.

How to add a hyperlink in a Google Docs using a Google Script

I have always used the insertText() function, but now I want to write a link in my google docs. The ideal would be to be able to write in HTML, but I don't know how.. it seems that it is not possible with the insertText() function.
How can I do that ?
You should be able to use setFormula and the Hyperlink formula like so:
var value = '=HYPERLINK("www.google.com", "Google")';
SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName("Sheet1")
.getRange("A1")
.setFormula(value);
Edit:
Looks like I misread the question. Try this instead:
DocumentApp.getActiveDocument()
.getBody()
.editAsText()
.insertText(0, "link text")
.setLinkUrl("www.google.com");
Edit 2: Looks like .setLinkUrl() is effecting the whole body, not the text inserted. If you put the link text into a variable and use the length of the variable to mark the link area, it should work. Try this instead:
function insertLink() {
var text = "link text\n";
var url = "www.google.com";
DocumentApp.getActiveDocument()
.getBody()
.editAsText()
.insertText(0, text)
.setLinkUrl(0, text.length, url);
}
To add a hyperlink in a document use Body.appendParagraph with setLinkUrl, then merge.
let doc = DocumentApp.create("My Document");
let body = doc.getBody();
body.appendParagraph("Please click ");
let link = body.appendParagraph("here").setLinkUrl("http://www.google.com");
link.merge();
let closing = body.appendParagraph(".");
closing.merge();
The code above will create a document with text that looks like:
Please click here.
You can also use the function below to make all the links in your document clickable.
function makeLinksClickable(document) {
const URL_PATTERN="http[^\s]+"
const URL_PATTER_LENGTH_CORECTION = "http ".length
const body = document.getBody()
var foundElement = body.findText(URL_PATTERN);
while (foundElement != null) {
// Get the text object from the element
var foundText = foundElement.getElement().asText();
// Where in the element is the found text?
const start = foundElement.getStartOffset();
const end = foundElement.getEndOffsetInclusive() - URL_PATTER_LENGTH_CORECTION;
const url = foundText.getText().substring(start,end)
//make url clickable
foundText.setLinkUrl(start, end, url)
// Find the next match
foundElement = body.findText(URL_PATTERN, foundElement);
}
}
If you are looking to find a string with a hyperlink, the following code will work.
function insertLink(){
const body = DocumentApp.getActiveDocument().getBody()
const text = body.findText('{{googleLink}}').getElement().asText()
text.setText('Link to google')
text.setLinkUrl('www.google.com')
}
I am using this script, this is working Calomun 1 Row > 2.
function InsertLink(e)
{
var actSht = e.source.getActiveSheet();
if (actSht.getName() == ['SheetName']){
var activeCell = actSht.getActiveCell(); //Detec the ActiveCell
//var activeCell = event.range;
var activeCellValue = e.value;
var column = activeCell.getColumn();
var colNums = [1]; //Columns, whose edit is considered
if(colNums.indexOf(column) == -1) return; //If column other than considered then return
var row = activeCell.getRow();
if(row < 2) return; //If header row then return
var length = String(activeCellValue).length;
if (!e.value)
{
activeCell.setValue()
}
else if(length > 4)
{
activeCell.setValue('=HYPERLINK' + '("http://otrs/otrs/index.pl?Action=AgentTicketZoom;TicketNumber='+activeCellValue+'";"'+activeCellValue+'")' );
}
}
}

Remove line breaks using apps scripts in a Google Document

Trying to work out how to remove multiple line breaks from Google Documents (not spreadsheets).
I've tried this and many variations thereof:
function searchAndReplace() {
var bodyElement = DocumentApp.getActiveDocument().getBody();
bodyElement.replaceText("\r\r", '\r');
}
Any idea please?
Noob to all of this...
Purpose is to replicate the search and replace in MS Word for ^p
Here is a rather "radical" method if your document has only paragraphs with text (images or other elements will be lost). See doc about element types here
(comments in code)
function removeNewLines(){
var doc = DocumentApp.getActiveDocument();
var text = doc.getBody().getText();// get a string
var textMod=text.replace(/\n/g,'');// replace all \n with ''
Logger.log(textMod);//optional check in logger
doc.getBody().clear().appendParagraph(textMod);// empty the doc and apend new texr
doc.saveAndClose();// save the result
}
I wanted to do the same thing (replace two new lines with a single new line). Ended up with the following as replaceText() doesn't accept \n for some reason.
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var text = body.editAsText();
var text_content = text.getText();
for(var i = 0, offset_i = 0; i < (text_content.length); i++){
if((text_content.charCodeAt(i)==10) && (text_content.charCodeAt(i-1)==10)){
text.deleteText(i-1-offset_i, i-1-offset_i)
offset_i++;
}
}
}
This code helped me to remove doubled new lines in document:
function removeDoubleNewLines(){
var doc = DocumentApp.getActiveDocument();
var paragraphs = doc.getBody().getParagraphs();
var paragraph;
for (var i = 0; i < paragraphs.length-1; i++) {
paragraph = paragraphs[i];
if(paragraph.getText() === '' &&
paragraph.getNumChildren() === 0) {
paragraph.removeFromParent();
}
}
}

cursorPos.insertInlineImage() gives error: "We're sorry, a server error occurred. Please wait a bit and try again"

I'm trying to use a script contained within a Google document to load an image from a url and insert it at the cursor position, but inserting the image with cursorPos.insertInlineImage(image) gives the error We're sorry, a server error occurred. Please wait a bit and try again
Inserting the image with getBody().appendImage(image) or inserting text with cursorPos.insertText("hello") works fine.
function onOpen(e) {
var ui = DocumentApp.getUi();
ui.createMenu("Test")
.addItem("Insert", "insertTest")
.addToUi();
}
function insertTest() {
var doc = DocumentApp.getActiveDocument();
var image = UrlFetchApp.fetch("http://latex.codecogs.com/gif.latex?E%3Dmc%5E2").getBlob();
var cursorPos = doc.getCursor();
doc.getBody().appendImage(image); // works
cursorPos.insertText("test!"); // works
cursorPos.insertInlineImage(image); // gives error
}
I'm happy to say found a simple workaround!
function insertTest() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var image = UrlFetchApp.fetch("http://latex.codecogs.com/gif.latex?E%3Dmc%5E2").getBlob();
var cursorElement = doc.getCursor().getElement();
body.insertImage(body.getChildIndex(cursorElement), image);
}
Unfortunately, if the cursor is inside a paragraph, this will throw an error saying cursorElement is not a child of body. The solution is to pass the upper-most level parent of cursorElement to getChildIndex.
function insertTest() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var image = UrlFetchApp.fetch("http://latex.codecogs.com/gif.latex?E%3Dmc%5E2").getBlob();
var cursorElement = doc.getCursor().getElement();
body.insertImage(body.getChildIndex(getTopLevelParent(cursorElement)), image);
}
// get the uppermost level parent of an element within a body section
function getTopLevelParent(element) {
var parent = element.getParent();
if (parent.getType() == DocumentApp.ElementType.BODY_SECTION) {
return element;
} else {
return getTopLevelParent(parent);
}
}
Here's a slightly simpler workaround.
The idea is to append the image to the end of the body of the document, then move it to the cursor position.
In the process, whatever it was about the original codecogs image that prevented insertInlineImage from working before is expunged and everyone is happy!
function insertTest() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var image = UrlFetchApp.fetch(
"http://latex.codecogs.com/gif.latex?E%3Dmc%5E2")
.getBlob();
var cursorPos = doc.getCursor();
var imageCopy = (image = body.appendImage(image)).copy();
cursorPos.insertInlineImage(imageCopy);
image.removeFromParent();
}

Can I color certain words in Google Document using Google Apps Script?

I'm trying to highlight certain words in my Google Document. I know I can replace text using document.replace, but it only replaces string itself, not formatting. Is there a way to replace string with colored string using Google Apps Script?
With the introduction of document-bound scripts, it's now possible to make a text highlighting function that's invoked from a custom menu.
Surely THIS is the best answer now! 8^)
This script was modified from the one in this answer, and may be called from the UI (with no parameters) or a script.
/**
* Find all matches of target text in current document, and highlight them.
*
* #param {String} target (Optional) The text or regex to search for.
* See Body.findText() for details.
* #param {String} background (Optional) The desired highlight color.
* A default orange is provided.
*/
function highlightText(target,background) {
// If no search parameter was provided, ask for one
if (arguments.length == 0) {
var ui = DocumentApp.getUi();
var result = ui.prompt('Text Highlighter',
'Enter text to highlight:', ui.ButtonSet.OK_CANCEL);
// Exit if user hit Cancel.
if (result.getSelectedButton() !== ui.Button.OK) return;
// else
target = result.getResponseText();
}
var background = background || '#F3E2A9'; // default color is light orangish.
var doc = DocumentApp.getActiveDocument();
var bodyElement = DocumentApp.getActiveDocument().getBody();
var searchResult = bodyElement.findText(target);
while (searchResult !== null) {
var thisElement = searchResult.getElement();
var thisElementText = thisElement.asText();
//Logger.log(url);
thisElementText.setBackgroundColor(searchResult.getStartOffset(), searchResult.getEndOffsetInclusive(),background);
// search for next match
searchResult = bodyElement.findText(target, searchResult);
}
}
/**
* Create custom menu when document is opened.
*/
function onOpen() {
DocumentApp.getUi().createMenu('Custom')
.addItem('Text Highlighter', 'highlightText')
.addToUi();
}
This is a better solution:
function highlightTextTwo() {
var doc = DocumentApp.openById('<your document id');
var textToHighlight = 'dusty death';
var highlightStyle = {};
highlightStyle[DocumentApp.Attribute.FOREGROUND_COLOR] = '#FF0000';
var paras = doc.getParagraphs();
var textLocation = {};
var i;
for (i=0; i<paras.length; ++i) {
textLocation = paras[i].findText(textToHighlight);
if (textLocation != null && textLocation.getStartOffset() != -1) {
textLocation.getElement().setAttributes(textLocation.getStartOffset(),textLocation.getEndOffsetInclusive(), highlightStyle);
}
}
}
Previous Answer:
The key is to being able to reference just the words you want to color.
My solution is to:
Get the text of the paragraph that contains the words you wish to color, remove the original paragraph, then add each part of the text back. As you add each part back the appendText returns a reference to just the text added, you then can specify its color with setForegroundColor():
function highlightText() {
var doc = DocumentApp.openById('<your document id>');
var textToHighlight = 'dusty death';
var textLength = textToHighlight.length;
var paras = doc.getParagraphs();
var paraText = '';
var start;
for (var i=0; i<paras.length; ++i) {
paraText = paras[i].getText();
start = paraText.indexOf(textToHighlight);
if (start >= 0) {
var preText = paraText.substr(0, start);
var text = paraText.substr(start, textLength);
var postText = paraText.substr(start + textLength, paraText.length);
doc.removeChild(paras[i]);
var newPara = doc.insertParagraph(i, preText);
newPara.appendText(text).setForegroundColor('#FF0000');
newPara.appendText(postText).setForegroundColor('#000000');
}
}
}
I think it's possible with the method setBackgroundColor of class Text in DocumentApp : https://developers.google.com/apps-script/class_text#setBackgroundColor
You'll have to retrieve your words as Text elements. In order to do that you can use the find method of your object Document, then to iterate over the search results and use getElement. Finally, to convert your Element object into a Text object, you can use asText().
Hope it'll work ! ;)
This is available as a Google docs add-on named Multi-instance Text Highlighting. Hints: At first it didn't seem to work, but I closed my doc and re-opened it, and then it worked. Then it didn't seem to work now and then, but I found out that special characters in your text string can break it; I think I had a + in my string and it just didn't do anything. But without special characters, it works great. Really helped me out.