Adding formatted text to a Google Doc - google-apps-script

I have a Google Sheet with form responses. I created some code so that each time a form is completed, the results populate a Google Doc. This works fine.
However, I want to add text to my Google Doc as well, which I accomplish using:
function myFunction(e) {
var doc = DocumentApp.create('File');
var text = 'insert text here';
body.appendParagraph(text);
doc.saveAndClose();
}
This text only is added as plain text, however, and I'd like to format this text. Specifically, I'd like to add the text so that it's bolded, underlined, and center-aligned in the document body.
How do I do this?
After some internet searching and SO searching, I tried adding html (e.g., <b> </b>) and I tried text.setBold(true). These approaches did not work.
I'll admit that I know very little about coding in Google Script editor, so I'm not at all sure how to go about this. I'm lucky enough that I got all my form responses to populate a named Google Doc file!

Here's a fragment of a document that I created recently:
var nameStyle={};
nameStyle[DocumentApp.Attribute.FONT_SIZE]=8;
nameStyle[DocumentApp.Attribute.BOLD]=true;
nameStyle[DocumentApp.Attribute.FOREGROUND_COLOR]='#000000';
var valStyle={};
valStyle[DocumentApp.Attribute.FONT_SIZE]=12;
valStyle[DocumentApp.Attribute.BOLD]=false;
valStyle[DocumentApp.Attribute.FOREGROUND_COLOR]='#cc0000';
body.appendParagraph('Basic Project Data').setAttributes(hdg1Style);
var p1=body.appendParagraph(Utilities.formatString('%s: ','PID')).setAttributes(nameStyle);
var p2=body.appendParagraph(Utilities.formatString('%s',selObj.pid)).setAttributes(valStyle);
p2.merge();
for(var i=0;i<basicDataA.length;i++){
var par1=body.appendParagraph(Utilities.formatString('%s: ',basicDataA[i][0])).setAttributes(nameStyle);
var par2=body.appendParagraph(Utilities.formatString('%s',basicDataA[i][1])).setAttributes(valStyle);
par2.merge();
}
Note, the appendParagraph first and then setAttributes.

The above response got me on the right path (Thanks!). Google hasn't documented this feature very well, and some of the features that should work do not. I had found the info on formatting using offsets but that is incredibly tedious and verbose. Here's the full example method that works to insert pre-formatted text into a Google Doc. Hope it helps someone else. Obviously the 2D array can instead be wired up to be fetched from a form, but this will at least show how the rest of the formatting works.
// Open a document by ID.
var body = DocumentApp.openById('YourDocId').getBody();
var authorAffils = [['Tony Tiger',1],['Micky Mouse',2],['Daffy Duck',3],['Elmo Orange',4]];
var nameStyle={};
nameStyle[DocumentApp.Attribute.FONT_SIZE]=11;
// nameStyle[DocumentApp.TextAlignment.Normal]; // This seems to do nothing
nameStyle[DocumentApp.Attribute.FOREGROUND_COLOR]='#000000';
var affilStyle={};
affilStyle[DocumentApp.Attribute.FONT_SIZE]=11;
// affilStyle[DocumentApp.TextAlignment.SUPERSCRIPT]; // This seems to do nothing
affilStyle[DocumentApp.Attribute.FOREGROUND_COLOR]='#cc0000';
for(var i=0;i<authorAffils.length;i++){
var par1=body.appendParagraph(Utilities.formatString(' %s,',authorAffils[i][0])).setAttributes(nameStyle);
var par2=body.appendParagraph(Utilities.formatString('%s',authorAffils[i][1])).setAttributes(affilStyle);
// I am not entirely clear why alignment only works this way whereas font size and color work the other way.
par1.setTextAlignment(DocumentApp.TextAlignment.NORMAL).merge();
par2.setTextAlignment(DocumentApp.TextAlignment.SUPERSCRIPT).merge();
}

Related

Google Script: Inserting text in a certain position

This is the code I have:
doc.getBody().editAsText().insertText(0, 'Google').setLinkUrl(0, text.length, 'www.google.com');
This works but inserts the text into the very top of the page. How can I set to where I am in the code?
Found the answer:
var link = body.appendParagraph("Google")
link.setLinkUrl("https://www.google.com");

Using documentApp Text class methods in a google sheet

I have a range with rich text hyperlinks in it in a googlesheet, copied and pasted from elsewhere.
I want to create a function to extract the URLs - there's a method called .getLinkUrl in the text class that I think will do the trick.
However, SpreadsheetApp doesn't have a Text class, so I think I need the .getLinkUrl method from the Text class in DocumentApp.
I'd like to do something like this:
function getURL(range) {
var textElement = DocumentApp.create("temp").getBody().editAsText()
var url = textElement.setText(range.getValue()).getLinkUrl;
return url
}
But you can't do that because you can't create documents in custom functions.
I'd really like to avoid having to use a triggered script or something instead of a function but I can't work out how to do it without.
Any ideas?

Need to read a single value from text file to speedometer

I have with me the canvas speedometer zip file. My intent is to read, from a text file a single number, and accordingly move the needle on the canvas speedometer as per the number read.
I have tried a few things and all I have managed to do is clunk up things a bit further. Any simple way to get around this, please? Preferably, editing the speedometer.html file such and adding a single button that reads the number from the text file and populates it in the text area provided on the speedometer web page?
Please help.
Edit 1: My bad. I should have included the piece of code I fidgeted around with for a brief while. Here goes the javascript snippet:
function func1()
{
var fileToLoad = document.getElementById("fileToLoad").files[0];
var fileReader = new FileReader();
fileReader.onload = function(fileLoadedEvent)
{
var textFromFileLoaded = fileLoadedEvent.target.result;
document.getElementById("maxvalue").value = textFromFileLoaded;
};
fileReader.readAsText(fileToLoad, "UTF-8");
}
And here's the bit about the 'Extract Value' button:
<input type="file" id="fileToLoad">
<button onclick="func1()">Extract Value</button>
The issue is I am getting sort of a clunky page. One which has a choose file button, and the 'Extract Value' button. And then, there are the begin and start buttons defined in the canvas speedometer API. All I am asking is this: is there a way by which I can have a single button on the page on clicking which the data (a single number) present in the text file gets populated on the ext field in the speedometer web page. This is all I want.
And in case anybody is wondering what the speedometer thingy is, please look here:
{http://www.uibox.in/item/68}
Apologies again for leaving a clearly vague question.

Changing text attributes (font, size, etc) at the cursor level in a google doc using Google apps script

I would like know if and how it is possible to change text attributes such as font, size, etc. in Google Apps script at the cursor's level. The script is bound to a Google doc file. For example, after running the script, the text font will change for anything written after that point while leaving the text written before unchanged. This is to mimic the way built-in styles or font menus behave in Google docs.
Here is what I came up with so far. It seems to change the text font globally in the document instead of applying the changes only to the text written after running the code. Any suggestions?
var cursor = DocumentApp.getActiveDocument().getCursor();
if(cursor){
var element4 = cursor.getElement()
var body = DocumentApp.getActiveDocument().getBody()
if (element4.editAsText) {
body.editAsText().setFontFamily(DocumentApp.FontFamily.CALIBRI);
}
}
The code below changes the FontFamily for the paragraph in which you select a text... it keeps the same style for all what is coming after and preserves everything before.
If you want to go deeper in precision you'll have to play with offsets and work at text level inside the paragraph but I thought this version could be sufficient.
function setStyle() {
var selection = DocumentApp.getActiveDocument().getSelection();
if (!selection) {
DocumentApp.getUi().alert('Cannot find a selection in the document.');
return;
}
var selectedElements = selection.getSelectedElements();
var element = selectedElements[0].getElement().getParent();
element.setFontFamily(DocumentApp.FontFamily.CONSOLAS);
}
The DocumentApp.FontFamily enumeration is now deprecated. You should use string names, like "Consolas" (case sensitive!), instead.
You can visit the setAttributes section of the DocumentApp Reference found here

How do I use Google Apps Script to change a CSS page to one with inline styles?

Background...
I am trying to write a Google Apps Script to get the content of a Google Doc as HTML and use that HTML to create or update a web page in Google Sites. I already know how to do this but the result is a web page that is stripped of almost all of its formatting. After looking at the html from the Google Doc, I see that it is not using inline styles and I believe that Google Sites requires inline styling.
Anyone have a Google Apps Script that I can use to convert the CSS to inline styles before using it to create a Google Sites page? Also, a library that I could use within the Google Apps Script environment that would give me the same functionality would be just as good. It just needs to be a library that I could add within the Google Apps Scripting environment (i.e., through the "resources" - "manage libraries" menu). Thanks.
By the way...
I have tried getting the html from a Google Doc in two ways. Both ways give me the same CSS non-inline-style that gets stripped out when I use it to create a Google Sites Page.
1) I have used Romain Vialard's DocsListExtened Google Script Library at the following link...
https://sites.google.com/site/scriptsexamples/new-connectors-to-google-services/driveservice
2) I have used code suggested by a few people including hgabreu#gmail.com, and others at...
https://code.google.com/p/google-apps-script-issues/issues/detail?can=2&start=0&num=100&q=&colspec=Stars%20Opened%20ID%20Type%20Status%20Summary%20Component%20Owner&groupby=&sort=&id=585
Note: the same problem affects html email messages sent to gmail users.
There are numerous online tools that do this conversion, so you could leverage one of them from Google Apps Script. (If you only need to do this once in a while, why not just use one of those services?)
Here's an example script, that builds on the getElementByVal() function from Does Google Apps Script have something like getElementById?.
inline() function
/**
* Convert html containing <style> tags to instead have inline css.
*
* This example uses an online service from MailChimp Labs, but
* the same principle could be used to leverage several other
* online providers.
*
* #param {Text} htmlWstyle A block of HTML text including
* <style>..</style> tags.
*
* #returns {Text} Same HTML, converted to inline css.
*/
function inline(htmlWstyle) {
// Generate a POST request to inline css web tool.
var payload =
{
"html" : htmlWstyle,
"strip" : "checked"
};
// Because payload is a JavaScript object, it will be interpreted as
// an HTML form. (We do not need to specify contentType; it will
// automatically default to either 'application/x-www-form-urlencoded'
// or 'multipart/form-data')
var options =
{
"method" : "post",
"payload" : payload,
"muteHttpExceptions" : true
};
var url = "http://beaker.mailchimp.com/inline-css";
var response = UrlFetchApp.fetch(url, options);
// The html from this service is non-compliant, so we need
// to massage it to satisfy the XmlService.
var badlink = new RegExp('<link (.*?)[\/]*>',"igm");
var badmeta = new RegExp('<meta (.*?)[\/]*>',"igm");
var badinput = new RegExp('<input (.*?)[\/]*>',"igm");
var xml = response.getContentText()
.replace(badlink,"<link $1></link>" )
.replace(badinput,"<input $1></input>" )
.replace(badmeta,"<meta $1></meta>" )
.replace(/<br>/g,"<br/>");
// So far, so good! Next, extract converted text from page. <textarea name="text" ...>
// Put the receieved xml response into XMLdocument format
var doc = XmlService.parse(xml);
var inlineHTML = getElementByVal( doc, 'textarea', 'name', 'text' );
return (inlineHTML == null) ? null : inlineHTML.getValue();
}
Explanation
There may appear to be some black magic in there. A previous answer described how to use the old Xml Service to examine the structure of a web page to find the bits you wanted. It's still good reading (and voting up, hint, hint!), but that service is now gone, and the new XmlService doesn't have equivalent exploratory support.
To start, we found a web service that did the job we were interested in, and used the UrlFetch Service to simulate a person pasting code into the service. Ideally, we'd like one that returned just the result we wanted, in a format we could use without any further work. Alas, what we had was a complete web page, and that meant that we'd have to farm it for our result. Basic idea there: Use the XmlService to parse and explore the page, extracting just the bit we wanted.
In the Css Inline service that was selected, Chrome's ability to "Inspect Element" was used to determine the tag type (<textarea>) and a way to uniquely identify it (name="text"). Armed with that knowledge, we had everything we needed to use getElementByVal() to dig through the HTML returned from a POST request. (Alternatively, you could use String methods to find and extract the text you want.)
But when that was all put together, XmlService kept complaining about the format of the HTML in the result page - so JavaScript String & RegExp methods were used to balance the malformed tags before passing the page on.
Example
Here's a simple example illustrating use of the inline() function. Note that style information is absorbed from both the external css link AND the tagged styling.
function test_inline() {
var myHtml =
'<html>'
+ '<head>'
+ '<title>Example</title>'
+ '<link rel="stylesheet" href="http://inlinestyler.torchboxapps.com/static/css/example.css" ></link>'
+ '</head>'
+ '<body>'
+ '<style type="text/css">'
+ 'h1{'
+ 'color:yellow'
+ '}'
+ '</style>'
+ '<h1>An example title</h1>'
+ '<p>Paragraph 1</p>'
+ '<p class="p2">Paragraph 2</p>'
+ '</body>'
+ '</html>';
var inlined = inline(myHtml);
debugger; // pause in debugger, have a look at "inlined"
}
Result
<html>
<head>
<title>Example</title>
</head>
<body>
<h1 style="color: yellow;">An example title</h1>
<p style="margin: 0;padding: 0 0 10px 0;">Paragraph 1</p>
<p class="p2" style="margin: 0;padding: 0 0 10px 0;">Paragraph 2</p>
</body>
</html>