Google Documents: set heading as defined in current document - google-apps-script

I'm writing a script that picks the paragraph where the cursor is contained, set the text to uppercase and change the paragraph heading to HEADING1.
However, the paragraph is set to the 'global' HEADING1, not to HEADING1 as it is defined in the current document. Here is the code.
function SetSceneHeading() {
var cursor = DocumentApp.getActiveDocument().getCursor();
var element = cursor.getElement();
var paragraph = [];
if (element.getType() != 'PARAGRAPH') {
paragraph = element.getParent().asParagraph();
}
else paragraph = element.asParagraph();
var txt = paragraph.getText();
var TXT = txt.toUpperCase();
paragraph.setText(TXT);
paragraph.setHeading(DocumentApp.ParagraphHeading.HEADING1);
}
Is there a way to set a paragraph to the 'current' HEADING1? Thanks.

I found a workaroud to set a paragraph to a user defined heading. Basically, you first set the heading using setHeading(), then you set to "null" the attributes that the previous operation messed up. This way the paragraph is set according to the user defined heading.
function MyFunction ()
var paragraph = ....
paragraph.setHeading(DocumentApp.ParagraphHeading.HEADING1);
paragraph.setAttributes(ResetAttributes());
function ResetAttributes() {
var style = {};
style[DocumentApp.Attribute.FONT_SIZE] = null;
style[DocumentApp.Attribute.BOLD] = null;
style[DocumentApp.Attribute.SPACING_BEFORE] = null;
style[DocumentApp.Attribute.SPACING_AFTER] = null;
return style;
}
I made a few tests, FONT_SIZE BOLD SPACING_BEFORE SPACING_AFTER seem to be the attributes that need to be reset. They may be more, according to the cases.

Unfortunately it seems that this won't be possible for now, there is an open issue that I think is relevant : issue 2373 (status acknowledged) , you could star it to get informed of any enhancement.

Related

Centering in a gDoc after a replace

I am looking to replace a string within a Google Doc via an app script. The string will exist on a line, but after the replace, I want it to have a specific font, size and justification.
I've created a style to address all these attributes (I included both Horiz. and Vert. alignment) and most of it works fine. When the string is replaced, the replacement has the right font, size and bold attributes. For some reason, I cannot get the justification to get changed.
// Define the style for the replacement string.
var hdrStyle = {};
hdrStyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =
DocumentApp.HorizontalAlignment.CENTER;
hdrStyle[DocumentApp.Attribute.VERTICAL_ALIGNMENT] =
DocumentApp.VerticalAlignment.CENTER;
hdrStyle[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';
hdrStyle[DocumentApp.Attribute.FONT_SIZE] = 24;
hdrStyle[DocumentApp.Attribute.BOLD] = true;
{ then later }
documentBody = DocumentApp.openById(fileId).getBody();
hdrElem = documentBody.findText("old string").getElement();
hdrText = hdrElem.asText().setText("new string");
// Force our 'header style':
hdrElem.setAttributes(hdrStyle);
I've tried setting the style after the findText and (as here) after, but no change in centering.
I see there is a paragraph centering, but I am not clear how to 'get' the paragraph associated with the element that is returned on the find.
I'm hoping this is some simple set of calls - but have run out of ideas (and patience)..!
Any help would be appreciated!
You can use getParent() on hdrElem to get the parent paragraph to apply the styling to.
https://developers.google.com/apps-script/reference/document/text#getParent()
documentBody = DocumentApp.openById(fileId).getBody();
hdrElem = documentBody.findText("old string").getElement();
hdrText = hdrElem.asText().setText("new string");
var hdrParent = hdrElem.getParent()
// Force our 'header style':
hdrParent.setAttributes(hdrStyle);

setAttributes does not apply foreground color, but bold and other formatting is retained

I want to replace a word "allowance" with "Some text", after running the code, It will remove word allowance and apply "Some text" with same formatting as that of "allowance" but foreground color property is not getting set as that of original.I want Some text also in red color as shown in the screenshot
function retainFormatting() {
var doc = DocumentApp.getActiveDocument();
var textToHighlight = 'allowance';
var highlightStyle;
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) {
highlightStyle = textLocation.getElement().getAttributes(textLocation.getStartOffset());
textLocation.getElement().deleteText(textLocation.getStartOffset(),textLocation.getEndOffsetInclusive());
textLocation.getElement().insertText(textLocation.getStartOffset(),"Some text");
textLocation.getElement().setAttributes(textLocation.getStartOffset(),textLocation.getEndOffsetInclusive(), highlightStyle);
}
}
}
before setting attribute at offset
after setting attribute it turns out to be
getForegroundColor(offset)
Retrieves the foreground color at the specified character offset.
And
setForegroundColor(startOffset, endOffsetInclusive, color)
Sets the foreground color for the specified character range.
Here is a sample code :
Getting Color from text
highlightColor = textLocation.getElement().getForegroundColor(textLocation.getStartOffset());
Applying color to text
textLocation.getElement().setForegroundColor(textLocation.getStartOffset(),textLocation.getEndOffsetInclusive(), highlightStyle);
I hope it helps. Goodluck :)
Try
textLocation.getElement().editAsText().deleteText(textLocation.getStartOffset(),textLocation.getEndOffsetInclusive());
textLocation.getElement().editAsText().insertText(textLocation.getStartOffset(),"Some text");
The .editAsText() puts you into editing the contents of the rich text leaving the existing attributes as a 'wrapper'
Alternatively, try replacing the text rather than deleting and inserting
paras[i].replaceText("allowance", "some text") // the first attribute is a regular expression as string
I have just tested this and it seems that setting LINK_URL alongside other attributes interferes with FOREGROUND_COLOR.
The following results in a black text color:
var attrs = {
"FOREGROUND_COLOR": "#ff0000", // should be red
"LINK_URL": null
};
text.setAttributes(start, end, attrs);
The following results in a red text color:
var attrs = {
"FOREGROUND_COLOR": "#ff0000" // should be red
};
text.setAttributes(start, end, attrs);
In effect, if you don't need to set the link, remove the LINK_URL from the list of formatting options.
#JSDBroughton Gave me an idea, which worked.
Try setting the attributes of the rich text object you get when calling editAsText. So instead of:
highlightStyle = textLocation.getElement().getAttributes(textLocation.getStartOffset());
textLocation.getElement().setAttributes(textLocation.getStartOffset(),textLocation.getEndOffsetInclusive(), highlightStyle);
Do:
// Make sure you replace `asParagraph` with what you actually need
highlightStyle = textLocation.getElement().asParagraph().editAsText().getAttributes(textLocation.getStartOffset());
textLocation.getElement().asParagraph().editAsText().setAttributes(textLocation.getStartOffset(),textLocation.getEndOffsetInclusive(), highlightStyle);
Edit: after playing around with this, seems like this only sometimes works. I still haven't figured out the pattern for when it does work and when it doesn't.

Google Apps Script: weird page layout in a script formatted document

I'm working on a script that applies custom headings to a plain text document imported in Google Docs. The scripts works pretty much as it should. However the resulting document has a weird layout, as if random page breaks were inserted here and there. But there are no page breaks and I can't understand the reason of this layout. Checking the paragraph attributes give me no hints on what is wrong.
Here is the text BEFORE the script is applied:
https://docs.google.com/document/d/1MzFvlkG13i3rrUcz5jmmSppG4sBH6zTXr7RViwdqaIo/edit?usp=sharing
You can make a copy of the document and execute the script (from the Scripts menu, choose Apply Headings). The script applies the appropriate heading to the scene heading, name of the character, dialogue, etc.
As you can see, at the bottom of page 2 and 3 of the resulting document there is a big gap and I can't figure out why. The paragraph attributes seem ok to me...
Here is a copy of the script:
// Apply headings to sceneheadings, actions, characters, dialogues, parentheticals
// to an imported plain text film script;
function ApplyHeadings() {
var pars = DocumentApp.getActiveDocument().getBody().getParagraphs();
for(var i=0; i<pars.length; i++) {
var par = pars[i];
var partext = par.getText();
var indt = par.getIndentStart();
Logger.log(indt);
if (indt > 100 && indt < 120) {
var INT = par.findText("INT.");
var EXT = par.findText("EXT.");
if (INT != null || EXT != null) {
par.setHeading(DocumentApp.ParagraphHeading.HEADING1);
par.setAttributes(ResetAttributes());
}
else {
par.setHeading(DocumentApp.ParagraphHeading.NORMAL);
par.setAttributes(ResetAttributes());
}
}
else if (indt > 245 && indt < 260) {
par.setHeading(DocumentApp.ParagraphHeading.HEADING2);
par.setAttributes(ResetAttributes());
}
else if (indt > 170 && indt < 190) {
par.setHeading(DocumentApp.ParagraphHeading.HEADING3);
par.setAttributes(ResetAttributes());
}
else if (indt > 200 && indt < 240) {
par.setHeading(DocumentApp.ParagraphHeading.HEADING4);
par.setAttributes(ResetAttributes());
}
}
}
// Reset all the attributes to "null" apart from HEADING;
function ResetAttributes() {
var style = {};
style[DocumentApp.Attribute.STRIKETHROUGH] = null;
style[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] = null;
style[DocumentApp.Attribute.INDENT_START] = null;
style[DocumentApp.Attribute.INDENT_END] = null;
style[DocumentApp.Attribute.INDENT_FIRST_LINE] = null;
style[DocumentApp.Attribute.LINE_SPACING] = null;
style[DocumentApp.Attribute.ITALIC] = null;
style[DocumentApp.Attribute.FONT_SIZE] = null;
style[DocumentApp.Attribute.FONT_FAMILY] = null;
style[DocumentApp.Attribute.BOLD] = null;
style[DocumentApp.Attribute.SPACING_BEFORE] = null;
style[DocumentApp.Attribute.SPACING_AFTER] = null;
return style;
}
A couple of screenshots to make the problem more clear.
This is page 2 of the document BEFORE the script is applied.
This is page two AFTER the script is applied. Headings are applied correctly but... Why the white space at the bottom?
Note: if you manually re-apply HEADING2 to the first paragraph of page 3 (AUDIO TV), the paragraph will jump back to fill the space at the bottom of page 2. This action, however, doesn't change any attribute in the paragraph. So why the magic happens?
Thanks a lot for your patience.
That was an interesting problem ;-)
I copied your doc, ran the script and had a surprise : nothing happened !
It took me a few minutes to realize that the copy I just made had no style defined for headings, everything was for some reason in courrier new 12pt, including the headings.
I examined the log and saw the indent values, played with that a lot to finally see that the headings were there but not changing the style.
So I went in the doc menu and set 'Use my default style and... everything looks fine, see screen capture below.
So now your question : it appears that there must be something wrong in your style definition, by "wrong" I mean something that changes more than just the font Style and size but honestly I can't see any way to guess what since I'm unable to reproduce it... Please try resetting your heading styles and re-define your default.... and tell us what happens then.
PS : here are my default heading styles : (and the url of my copy in view only :https://docs.google.com/document/d/1yP0RRCrRSsQc9zCk-sdfu5olNGDkoIrabXanII4qUG0/edit?usp=sharing )

Proper way to use style attributes

I'm using DocumentApp.Attribute with mixed results. Here is an example:
var underline = {};
underline[DocumentApp.Attribute.UNDERLINE] = true;
underline[DocumentApp.Attribute.WIDTH] = 100;
underline[DocumentApp.Attribute.MARGIN_LEFT] = 10;
doc.appendParagraph("Paragraph text").setAttributes(underline);
The paragraph is created, and underlined, but the other two attributes don't get applied.
I think that you will find that a paragraph cannot have either Margin or width attributes ... they apply to the page or document as a whole. You might get the effect that you wish by using the Indent set of attributes.
This begs the next question "how do you set page attributes?"
MARGIN-LEFT appears as an attribute of the Body section so getActiveSection().setAttributes(style)
I am not sure what width refers to but you can do a getAttributes for each element type to track it down PAGE-WIDTH is an attribute of Body Section again. Play around with this code ...
function myFunction() {
var doc = DocumentApp.openById("1lqjkdfdsafgdsafsdaQI3kjtY");
var docele = doc.getActiveSection();
Logger.log(docele.getAttributes());
var para = doc.getParagraphs()[0];
var atts = para.getAttributes();
Logger.log(atts)
// Define a custom paragraph style.
var style = {};
style[DocumentApp.Attribute.WIDTH] = 100;
style[DocumentApp.Attribute.MARGIN_LEFT] = 200;
docele.setAttributes(style);
}
For me this gave body section attributes of {UNDERLINE=null, MARGIN_BOTTOM=72.0, PAGE_HEIGHT=792.0, BOLD=null, BACKGROUND_COLOR=null, FONT_SIZE=null, FONT_FAMILY=null, STRIKETHROUGH=null, MARGIN_LEFT=10.0, PAGE_WIDTH=612.0, LINK_URL=null, ITALIC=null, MARGIN_RIGHT=72.0, MARGIN_TOP=72.0, FOREGROUND_COLOR=null}
and paragraph attributes of {UNDERLINE=null, INDENT_END=8.25, LEFT_TO_RIGHT=true, BOLD=null, BACKGROUND_COLOR=null, FONT_SIZE=12, FONT_FAMILY=Comic Sans MS, SPACING_BEFORE=null, SPACING_AFTER=null, STRIKETHROUGH=null, INDENT_START=0.0, LINE_SPACING=null, LINK_URL=null, ITALIC=null, INDENT_FIRST_LINE=0.0, HORIZONTAL_ALIGNMENT=null, HEADING=null, FOREGROUND_COLOR=null}
This gives a clue to the alternative form for setting of attributes
docele.setAttributes({"FOREGROUND_COLOR":"#ff0000"})

xul-Get selection html

I have the following function that is supposed to get HTMLs for the user selected area on the web page. This function does not seems to work properly.
Sometime, it gets htmls which is not selected also.
Can anyone please look into this function? -- Thanks a lot.
//----------------------------Get Selected HTML------------------------
function getSelectionHTML(){
if (window.getSelection)
{
var focusedWindow = document.commandDispatcher.focusedWindow;
var sel = focusedWindow.getSelection();
var html = "";
var r = sel.getRangeAt(0);
var parent_element = r.commonAncestorContainer;
var prev_html = parent_element.innerHTML;
if(prev_html != undefined)
{
return prev_html;
}
return sel;
}
return null;
}
It looks to me like you're getting the contents of the parent element rather than the selection itself. If the parent element contains anything other than what you have selected, then you'll get that too.
var sel = focusedWindow.getSelection();
This line returns a selection object. It contains the exact text selected by the user. You then get the range from the selection and get the commonAncestorContainer. So if you have code like this:
<div id="ancestor">
<p>First sentence.</p>
<p>Another sentence.</p>
</div>
And your user selects from the 's' of the first sentence to the 's' of the second sentence then the commonAncestorContainer is the div element so you'll also get the rest of the text.
A good reason for this would be if you wanted to guarantee yourself a valid HTML fragment (this seems to be the case, implied by your function name), but if you just want the selected text then call the toString method on the range directly:
var focusedWindow = document.commandDispatcher.focusedWindow;
var sel = focusedWindow.getSelection();
var r = sel.getRangeAt(0);
return r.toString();