setAttributes in Google Doc header failing - setAttibutes is not a function - google-apps-script

I want to set the font size in a google doc header, but I get the error 'setAttributes is not a function'. I can't really understand the structure of a header (it seems), as to where to set the attributes.
var style = {};
style[DocumentApp.Attribute.FONT_SIZE] = 16;
style[DocumentApp.Attribute.BOLD] = true;
var range = this.Doc.getHeader(
range.setText(text)
var m = range.getNumChildren();
for (var i =0; i<m;i++){
var cld = range.getChild(i);
var ct = cld.getText();
var cat = cld.getAttributes();
cld.setAttibutes(style);
}
In the code above I can set the text in the header, and I can see the text in the 1st child element "ct", but I can't set the attributes. cld.getAttributes() returns nulls, so I'm thinking the attributes are set on a higher element. I just don't know which.

Issue:
getChild returns element, not Text, thus you need to change cld to Text via asText and then use getText().
As for the setAttributes function, I didn't encounter the issue you had, it just worked so I have no clue on why it errors on yours.
Can you try this one?
Code:
function updateHeader() {
var doc = DocumentApp.getActiveDocument();
var range = doc.getHeader();
var text = "this is the new header";
var style = {};
style[DocumentApp.Attribute.FONT_SIZE] = 16;
style[DocumentApp.Attribute.BOLD] = true;
var m = range.getNumChildren();
for (var i = 0; i < m; i++){
var cld = range.getChild(i);
var ct = cld.asText().getText(); // get text value
var cat = cld.getAttributes(); // get attributes
Logger.log(ct); // print old text
Logger.log(cat); // print old attributes
cld.asText().setText(text); // set text as header value
cld.setAttributes(style); // set attributes
Logger.log(cld.asText().getText()); // print new text
Logger.log(cld.getAttributes()); // print new attributes
}
}
Sample:
Output:
Logs:
Note:
I placed setText inside the loop to show you the difference of before and after the update of the value of the text and attribute.

Related

Adding Incrementing Citation ID

I've tried searching the issue and came up with nothing, so I'm looking for help.
What I'm trying to do is add incrementing IDs to individual citations by:
Searching a Google Doc for a specific combination of characters that signal the end of the citation (checking to see if there are any in the document at all).
If the characters are there, I'd like to find the first one, then place a '1' in between the two characters. Then the second one should have a '2' between it, the third should have a '3', and so on and so forth ensuring that all of the sets of characters have been replaced with unique number IDs
What is going wrong currently:
Due to the elemental structure of the Google Doc, my script is replacing ALL instances within a paragraph with the same ID number, which can be 1 instance or it can be 20. When it moves to the next paragraph, every found instance of the character combination is getting the incremented ID.
I need a genius' help.
Here's the code base I've found that I've been struggling to modify:
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var foundElement = body.findText("]]");
var i = 1;
while (foundElement != null) {
// Get the text object from the element
var foundText = foundElement.getElement().asText();
// Where in the Element is the found text?
var start = foundElement.getStartOffset();
var end = foundElement.getEndOffsetInclusive();
// Change the text
foundText.replaceText("]]","]"+i+"]");
// Find the next match
foundElement = body.findText("]]", foundElement);
i++
}
Issue
replaceText() will replace all the occurence in the element.
Solution
To avoid this, you should use the start and end to delete and insert text.
Please see my code below. This worked on my end.
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var foundElement = body.findText("]]");
var i = 1;
while (foundElement != null) {
// Get the text object from the element
var foundText = foundElement.getElement().asText();
// Where in the Element is the found text?
var start = foundElement.getStartOffset();
var end = foundElement.getEndOffsetInclusive();
// Change the text
foundText.deleteText(start, end)
foundText.insertText(start, "]"+i+"]")
// Find the next match
foundElement = body.findText("]]", foundElement);
i++
}
As you can see, I just modified the line where you replaceText() and transform it to a combination of deleteText() and insertText() methods.
Try this:
function myfunction() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var foundElement = body.findText("]]");
var i = 1;
while (foundElement != null) {
var foundText = foundElement.getElement().asText();
var start = foundElement.getStartOffset();
var end = foundElement.getEndOffsetInclusive();
foundText.replaceText("]]", "]" + i++ + "]");
foundElement = body.findText("]]", end);//Modifed this and the end point of the last search. And the i++ will use the i as it currently is and then increment it after the current operation.
}
}
Each next search needs to start searching after the location of the preceeding element. range element getEndOffsetInclusive()

Get Image URL from Identified cell in table in Docs and replace with image

I am trying to append an Image from a user input (which is the url for an image located in their drive). I can append and replace the url once the image id has been identified but I can't seem to isolate the id from the url in my code.
function getMyImage() {
var ui = DocumentApp.getUi();
//Creates a prompt for the user so that the row with keys can be found
var response = ui.prompt('In what row are the pictures or links found? (Please indicate a number)');
//Converts response to int and removes one to fit indexing
var rowNum = response.getResponseText() - 1
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
//Gets all tables in document
var tables = body.getTables();
//Passes through each table
for (var i = 0; i < tables.length; i++){
//Using this cell in particular determined by prompt result and 1, since DocAppener
var idCell = tables[i].getRow(rowNum).getCell(1);
//**Up to here everything works**
var url = idCell.getText();
function getIdFromUrl(url) {
return url.match(/[-\w]{25,}/);
}
//Find the image
var image = DriveApp.getFileById(url)
//**Past here everything works**
//Remove the text in the cell and replace it with image
var appendedImage = idCell.clear().appendImage(image.getBlob());
//Set height and width to create a standard for all images
var iHeight = appendedImage.getHeight()
var Ratio = iHeight/300
var iWidth = appendedImage.getWidth()/Ratio
//Calling height and width as well as set url
appendedImage.setHeight(300).setWidth(iWidth).setLinkUrl(imageurl)}
}
This is my document in question. https://docs.google.com/document/d/1FfzIm9GFhftgsTp7ZEd2l7ZywGVn0yVWZHaQFTUdHvk/edit?usp=sharing
I think the main problem is you don't seem to call your getIdFromUrl function, and simply call DriveApp.getFileById(url).
I think you want to do this:
var url = idCell.getText();
function getIdFromUrl(url) {
return url.match(/[-\w]{25,}/);
}
var id = getIdFromUrl(url)[0]; // <-- Add this
//Find the image
var image = DriveApp.getFileById(id) // <-- Get id
getIdFromUrl seems to return a list with one item, which is why I take the zero index of the result.

Replace text in Google Doc table for form merge

I am trying to make automated reports by filling a form. The data is read out by the script from the last spreadsheet line. Then the report is made as a Google Doc. Here some tags inside this document present where the items should be. %meterx% is for meters.
These can be images or text. For normal paragraphs, this is working fine with the first loop where the type = paragraph. But it skips tables. I need to replace the %meterx% in a table cell with the image, just like I do with the paragraph, but I am stuck at the code to look through the table.
I see some ways to replace text but this seems to be the only way to replace it with images.
var totalElements = doc.getNumChildren();
var el=[]
for( var j = 0; j < totalElements; ++j ) {
var element = doc.getChild(j);
var type = element.getType();
if (type =='PARAGRAPH'){
el[j]=element.getText()
if(el[j]=='%meter3%'){element.removeFromParent();
var newimage = UrlFetchApp.fetch('http://chart.googleapis.com/chart?chf=bg,s,67676700&chs=280x150&cht=gm&chds=0,10&chd=t:'+row[4]+'&chdlp=b').getBlob();
doc.insertImage(j, newimage);
if (type =='TABLE'){
var tablerows=element.getNumRows();
Logger.log(tablerows);
for ( var i = 0; i < tablerows; ++i ) {
var tablerow = element.getRow(0)
Logger.log(tablerow); // <--- gives TableRow
} /// STUCK !! :)
A TableRow contains child elements, of type TableCell, which also contain child elements. Those are type PARAGRAPH if they are blank or contain text.
You can access the text within a TableCell with getText(), but it's a good idea to confirm that the cell contains text first.
The code below handles PARAGRAPH and TABLE element types, and for tables it explores the TABLECELL elements. I don't know what you're doing with your array el[], so I've left that out, and also commented out the image-replacement code - instead, I'm just logging the structure and content of the table for illustration. To complete your goal, you should replace the logging with the same match & replace behavior you have for PARAGRAPH.
NOTE: I am using a couple of helper functions not detailed here, which should be self-explanatory, getFileByName_() and elementTypeToText_().
function Q13869576() {
var folder = "StackOverflow";
var docname = "Q13869576.gdoc";
var docId = getFileByName_(folder, docname).getId();
var doc = DocumentApp.openById(docId);
var docBody = doc.getActiveSection();
var totalElements = doc.getNumChildren();
var el=[]
for( var j = 0; j < totalElements; ++j ) {
var element = doc.getChild(j);
var type = element.getType();
switch (type) {
case DocumentApp.ElementType.PARAGRAPH:
el[j]=element.getText()
if(el[j]=='%meter3%'){
Logger.log( "Found tag in paragraph" );
// element.removeFromParent();
// var newimage = UrlFetchApp.fetch('http://chart.googleapis.com/chart?chf=bg,s,67676700&chs=280x150&cht=gm&chds=0,10&chd=t:'+row[4]+'&chdlp=b').getBlob();
// doc.insertImage(j, newimage);
}
break;
case DocumentApp.ElementType.TABLE:
var tablerows=element.getNumRows();
Logger.log(tablerows);
for ( var row = 0; row < tablerows; ++row ) {
var tablerow = element.getRow(row)
for ( var cell=0; cell < tablerow.getNumCells(); ++cell) {
Logger.log( "Table Row("+row+") Cell ("+cell+")");
Logger.log(
elementTypeToText_(tablerow.getChild(cell).getType())
+" with child type "
+elementTypeToText_(tablerow.getChild(cell).getChild(0).getType()));
var celltext = tablerow.getChild(cell).getText();
Logger.log( "Text is ("+celltext+")" );
}
}
break;
}
}
}
This is an excerpt of the logs from a sample doc, with a table containing a cell with the %meter3% tag:
...
Table Row(1) Cell (2)
TABLE_CELL with child type PARAGRAPH
Text is (%meter3%)
...

Setting setAttribute() for line formatting, line_spacing, SPACING_AFTER, SPACING_BEFORE

I am trying to generate a report in a Google doc from a template file. When it copies the document it resets all of the formatting to the defaulted for the user and not what the format in the original doc is. I've tried the following to try and set the formatting on a both the document, tableRow and tableCell level though when the report is created the line spacing is 1.5 and there is a space after paragraph
var style = {};
style[DocumentApp.Attribute.SPACING_AFTER] =0;
style[DocumentApp.Attribute.SPACING_BEFORE] =0;
style[DocumentApp.Attribute.LINE_SPACING]=1;
var newrow= tables[2].insertTableRow(n).setBold(false).setAttributes(style);
if(j==0){
newrow.insertTableCell(0,reportDate).setPaddingBottom(0).setPaddingTop(0).setAttributes(style);
}
else{
newrow.insertTableCell(0,'').setPaddingBottom(0).setPaddingTop(0).setAttributes(style);
}
newrow.insertTableCell(0,values1[rowId1][1]+' '+values1[rowId1][2]).setPaddingBottom(0).setPaddingTop(0).setAttributes(style);
newrow.insertTableCell(0,'').setPaddingBottom(0).setPaddingTop(0).setAttributes(style);
doc.editAsText().setAttributes(style);
any suggestions on how to have the report follow these attributes?
I believe the SPACING_AFTER, SPACING_BEFORE & LINE_SPACING are not attributes associated with the TABLE_CELL object. You must reference the child PARAGRAPH in order to set these.
var style = {};
style[DocumentApp.Attribute.SPACING_AFTER] =0;
style[DocumentApp.Attribute.SPACING_BEFORE] =0;
style[DocumentApp.Attribute.LINE_SPACING]=1;
var newrow = tables[2]
.insertTableRow(n)
.setBold(false);
if (j == 0) {
newrow.insertTableCell(0,reportDate)
.setPaddingBottom(0)
.setPaddingTop(0);
}
else {
newrow.insertTableCell(0,'').setPaddingBottom(0).setPaddingTop(0);
}
newrow.insertTableCell(0,values1[rowId1][1]+' '+values1[rowId1][2])
.setPaddingBottom(0)
.setPaddingTop(0);
newrow.insertTableCell(0,'')
.setPaddingBottom(0)
.setPaddingTop(0);
var newrowTableCell = newrow.getChild(0);
var newrowParagraph = newrowTableCell.getChild(0).setAttributes(style);
As the set attributes was not setting the attributes and you can not cast any of the elements as paragraphs I resolved this problem by getting all of the paragraphs and setting them manually by adding this at the end
var p=doc.getParagraphs();
for(i=0;i<p.length; i++){
p[i].setLineSpacing(1).setSpacingAfter(0);
Do the users need to be able to edit the report, or just view it? Why not generate a pdf? Every time I've done it, it saves the formatting.
var file = DocsList.getFileById('1DIfn_wVpXSI4hU5zG43Fvp2ZdpUP_KqgtgFRT9NWJ7E');
var newFile = DocsList.copy(file, ename+'-'+reportDate+'Monthly Report');
var report=DocsList.createFile(newFile.getAs("application/pdf")).rename(newFile.getName() + ".pdf");
var a = report.getID();
var doc = DocumentApp.openById(a);

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.