search slide and get page element text - google-apps-script

My script searches one specific slide in a presentation. Then it gets the text from the first page element. This page element is a shape with only one word. After this, when I put strings before and after this page element text, there is a break in the text.
function readShapeText() {
var presentation = SlidesApp.getActivePresentation();
var slides = presentation.getSlides();
for (i = 0; i < slides.length; i++) {
if(slides[i].getObjectId() == 'mySlideId'){
var pageElement = slides[i].getPageElements()[0].asShape().getText().asString();
}
}
var myModifiedElement = "My_" + pageElement + "_is_cool";
}
The output is with a break, but I need in one line:
My_TestElement
_is_cool
How can I eliminate or suppress the break? And is there a better way to find a specific slide without using "for loop" f.e. like presentation.openSlideById(xxxxxx)?

How about these answers?
Q1: How can I eliminate or suppress the break?
It seems that the end of texts is always given \n. This can be also seen from values retrieved by Slides.Presentations.get(). So if you want to retrieve the values without \n, you can do it using replace("\n", "").
Q2: Is there a better way to find a specific slide without using "for loop" f.e. like presentation.openSlideById(xxxxxx)?
How about the following sample script? It retrieved the specific slide using filter(), because the key of objectId is included in the array. And replace("\n", "") was also used.
function readShapeText() {
var mySlideId = "mySlideId"; // Please input this.
var presentation = SlidesApp.getActivePresentation();
var slides = presentation.getSlides();
var slide = slides.filter(function(e){return e.getObjectId() == mySlideId})[0];
var pageElement = slide.getPageElements()[0].asShape().getText().asString().replace("\n", "");
var myModifiedElement = "My_" + pageElement + "_is_cool";
Logger.log(myModifiedElement)
}
If I misunderstand your question, I'm sorry.

Related

Remove Horizontal Line in Google Doc with Google Apps Script

I have a Google Doc with text followed by a Horizontal Line below. If the user selects "NO" from a ui.alert, I need to remove all this text (simple using regex) and the horizontal line. I have no clue how to remove this Horizontal Line via Google Apps Script. Can't find anything about it in the documentation. Anyone have any ideas? Thanks!
var regExpFirstBriefing = "[A-Z \(\)]{42}\\v+[A-Za-z\.\", ]*[\\v+]{1}"; // This accounts for all the text I need removed along with an extra new line. The horizontal line is the next line.
// Ask user if this is the first briefing
var responseFirstBriefing = ui.alert('Question here...' , ui.ButtonSet.YES_NO);
if (responseFirstBriefing == ui.Button.YES) {
document.replaceText(regExpFirstBriefing, '');
}
You want to remove the searched text in Google Document.
You want to delete "HORIZONTAL_RULE" below the text.
You want to run above when the user selects "NO" from a ui.alert.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this sample script? Although I'm not sure about your actual Document, from your explanation, I imaged about it and prepare a sample script. Please think of this as just one of several answers. The flow of this sample script is as follows.
Flow:
Search text is searched using findText().
Put the element of searched text in an array.
This array is used for deleting element.
Search "HORIZONTAL_RULE" below the searched text.
In this case, when "HORIZONTAL_RULE" doesn't adjacent the searched text, "HORIZONTAL_RULE" is searched by offsetValue. In this sample, it is searched up to 3 paragraph ahead.
When "HORIZONTAL_RULE" is found, the element is put to the array.
Delete elements in the array.
From your script, the searched text is cleared. In this case, the paragraph is not deleted.
From your question, about "HORIZONTAL_RULE", the paragraph is deleted.
When above flow is reflected to the script, it becomes as follows.
Sample script:
When you run the script, the texts searched with regExpFirstBriefing are cleared and "HORIZONTAL_RULE" below the text is also removed.
function myFunction() {
var document = DocumentApp.getActiveDocument(); // Added
var ui = DocumentApp.getUi(); // Added
var regExpFirstBriefing = "[A-Z \(\)]{42}\\v+[A-Za-z\.\", ]*[\\v+]{1}";
var responseFirstBriefing = ui.alert('Question here...' , ui.ButtonSet.YES_NO);
if (responseFirstBriefing == ui.Button.YES) {
document.replaceText(regExpFirstBriefing, '');
// I added below script.
} else if (responseFirstBriefing == ui.Button.NO) {
var offsetValue = 3; // When "HORIZONTAL_RULE" doesn't adjacent the searched text, "HORIZONTAL_RULE" is searched by "offsetValue". In this sample, it is searched up to 3 paragraph ahead.
var body = document.getBody();
var r = body.findText(regExpFirstBriefing);
var remove = [];
while (r) {
remove.push(r.getElement().asText())
var parentParagraph = body.getChildIndex(r.getElement().getParent());
var totalChildren = body.getNumChildren();
for (var offset = 1; offset <= offsetValue; offset++) {
if (parentParagraph + offset <= totalChildren) {
var nextParagraph = body.getChild(parentParagraph + offset);
if (nextParagraph.getType() === DocumentApp.ElementType.PARAGRAPH) {
var c = nextParagraph.asParagraph().getNumChildren();
for (var i = 0; i < c; i++) {
var childOfNextParagraph = nextParagraph.asParagraph().getChild(i);
if (childOfNextParagraph.getType() === DocumentApp.ElementType.HORIZONTAL_RULE) {
remove.push(childOfNextParagraph.asHorizontalRule());
break;
}
}
if (remove[remove.length - 1].getType === DocumentApp.ElementType.HORIZONTAL_RULE) {
break;
}
}
}
}
r = body.findText(regExpFirstBriefing, r);
}
for (var i = remove.length - 1; i >=0; i--) {
/////
// If you want to delete the paragraph of searched text, please delete this if statement.
if (remove[i].getType() === DocumentApp.ElementType.TEXT) {
remove[i].removeFromParent();
continue;
}
/////
remove[i].getParent().asParagraph().removeFromParent();
}
}
}
Note:
This script supposes that the regex of [A-Z \(\)]{42}\\v+[A-Za-z\.\", ]*[\\v+]{1} works for your Document.
If you want to delete the paragraph of searched text, please delete this if statement of as follows from above script.
if (remove[i].getType() === DocumentApp.ElementType.TEXT) {
remove[i].removeFromParent();
continue;
}
References:
findText(searchPattern, from)
removeFromParent()
Class HorizontalRule
If I misunderstood your question and this was not the result you want, I apologize. At that time, in order to correctly understand your situation, can you provide a sample Document you want to use? Of course, please remove your personal information. I would like to confirm the issue from it.

Deleting all content down from the second horizontal line in a document

I'm trying to create a script to delete all text/content downwards from a page. Below you can see the current document.
Currently, I have the script set-up so that it deletes everything down and including from a text of, "STARTHERE". However, I want it to delete down from the second horizontal line in the image, however, not including the line.
Any ideas on how to delete down from the second horizontal line?
What does deleteText startOffset and endOffsetInclusive actually mean? Is it like a line number or?
Previous Script:
function removeText() {
var body = DocumentApp.getActiveDocument().getBody();
var rangeElement = body.editAsText();
var start = "STARTHERE";
var end = "ENDHERE";
var rangeElement1 = DocumentApp.getActiveDocument().getBody().findText(start);
var rangeElement2 = DocumentApp.getActiveDocument().getBody().findText(end);
if (rangeElement1.isPartial()) {
var startOffset = rangeElement1.getStartOffset();
var endOffset = rangeElement2.getEndOffsetInclusive();
rangeElement1.getElement().asText().deleteText(startOffset,endOffset);
}
}
You'll need to change your approach completely, because findText only finds text, and a horizontal line is not text; it is a special type of document element, HorizontalRule.
(Since you asked: startOffset and endOffsetInclusive are character counts within an element; e.g., if the text "red" is found in a paragraph that consists of "A big red dog", then startOffset is 6 and endOffset is 9. None of this helps here)
Here is my approach: loop over the Paragraph elements, looking for those that contain a HorizontalRule element (with findElement method). Once we found two such paragraphs, delete all subsequent ones.
There is a catch in that Apps Script can't delete the last paragraph of a document; for this reason I append empty paragraph ahead of time, and do not delete it.
function removeAfterSecondLine() {
var body = DocumentApp.getActiveDocument().getBody();
body.appendParagraph('');
var para = body.getParagraphs();
var ruleCount = 0;
for (var i = 0; i < para.length - 1; i++) {
if (ruleCount >= 2) {
body.removeChild(para[i]);
}
else if (para[i].findElement(DocumentApp.ElementType.HORIZONTAL_RULE)) {
ruleCount++;
}
}
}

Set the Glyph Type to "Checkbox" in Google Apps Script

I'm adding list items to a Google Doc. I know GlyphType lets you set the bullet type:
var myArray = myObjects[i].myColumn.split(", ");
for (var i = 0; i < myArray.length; i++) {
body.appendListItem(myArray[i])
.setGlyphType(DocumentApp.GlyphType.BULLET)
.setLineSpacing(1.85)
.setIndentStart(40);
}
body.appendListItem("Text").setIndentStart(40);
But how can I set the bullet type to "checkbox"? It is one of the available options within GDocs:
http://www.ultraimg.com/images/ScreenShot.png
I suspect if I were editing an existing document with the glyph type already set, .appendListItem() wouldn't change the glyph type. But my project involves creating a GDoc from scratch and doesn't lend itself well to using a template (because the number of times the template text is used would need to be variable).
Unfortunately It seems to be not possible... below is a small test I tried on a doc with "square bullets" :
function myFunction() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var element = body.getChild(1).asListItem();
var attrs = element.getAttributes();
for (var att in attrs) {
Logger.log(att + " : " + attrs[att]);
}
}
And the result : they show up as "normal" bullets.

Google Apps Script :How to append a table in Document after a selection

Is it possible to append a table after a selection in Google Document using Apps Script?
Only example I can find is this:
var body = DocumentApp.getActiveDocument().getBody();
var table = body.appendTable();
...which means to the end of document.
Thanks
EDIT:
How I make selection is:
var selection = doc.getSelection();
which is basicaly what is selected by mouse drag selection (blued out) on document editor.
From there I start to iterrate:
var elements = selection.getSelectedElements();
var element = elements[0].getElement();
var startOffset = elements[0].getStartOffset(); // -1 if whole element
var endOffset = elements[0].getEndOffsetInclusive(); // -1 if whole element
This might be a part of PARAGRAPH.
Here is code that inserts a table at the current selection. It may need to be modified a little, but the point is; that it finds the child index of the body at the point of the selection.
function insertTableAtSelection() {
// insert table as selection
var theDoc = DocumentApp.getActiveDocument();
var selection = theDoc.getSelection();
Logger.log('selection: ' + selection);
if (selection) {
var elements = selection.getRangeElements();
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
Logger.log('element: ' + element.getElement());
};
var theElmt = element;
var selectedElmt = theElmt.getElement();
Logger.log('selectedElmt: ' + selectedElmt);
var parent = selectedElmt.getParent();
var insertPoint = parent.getChildIndex(selectedElmt);
Logger.log('insertPoint: ' + insertPoint);
var body = theDoc.getBody();
var table = body.insertTable(insertPoint + 1, [['one','two','three'],['yellow', 'green', 'red']]);
};
};
The insertPoint is increased by one:
insertPoint + 1
That gets the table just beyond the selection.
For anyone reading this post who may want to adapt this code, keep in mind that this code only completes if there is a selection; the user needs to have highlighted some amount of content for there to be a selection.
You can append a table anywhere in the document, you only need to get the container element of your search result and append the table to it.
Have a look at this post for example to see how documents are build (but there are more... search on this forum with Google-Apps-Script tag.

replacing strings in a document and undo

In a mailMerge script I'm working on I use .replaceText() to replace fields with their corresponding values in a database.
The interface allows to test in the document to see if the result is looking as expected and I need to have a 'UNDO' function to get my fields in their original position so that I can use it with other values.(this script is bounded to a document in a side bar, see this post for illustration)
The script below does that pretty well by keeping in memory the field names an their replacement values.
The only detail that bothers me is that I had to define a special "empty" label for fields that have no values in the current test data to prevent losing their track in the document.
(I used a numbered identifier like °vide12°).
This is working perfectly but it's not ideal since the document in test mode is not exactly a representation of the final document because of these °videXX° that I use...
The question is : does anyone have a better idea or another approach to "localize" the replacement data when there is no data in a less visible way ? (I know this sound weird... that's why I explain the whole situation :-)
Considering the way Google Docs are build I thought that I could get the complete element structure and rebuild the doc from that info but I'm afraid it won't be possible since the smallest element is a paragraph and fields are mainly just single words...
Here is the relevant part of the code I use, I added a few comments to make it (hopefully) clear.
function valuesInDoc(e){ // this function replaces the fields with database values
var app = UiApp.getActiveApplication();
var listVal = UserProperties.getProperty('listSel').split(',');
var replacements = [];
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var find = body.findText('#ch');
if(find == null){var ui = DocumentApp.getUi() ; ui.alert("Aucun champ (#chX#) trouvé dans le document... Veuillez insérer des identifiants aux endroits souhaités");return app};
var curData = UserProperties.getProperty('selItem').split('|');
var Headers = [];
var OriHeaders = UserProperties.getProperty('Headers').split('|');
for(n=0;n<OriHeaders.length;++n){
Headers.push('#'+OriHeaders[n]+'#');
}
var fctSpe = 0 ;
for(var i in Headers){if(Headers[i].indexOf('SS')>-1){fctSpe = i}}
for(var n=0;n<listVal.length;++n){
var realIdx = Number(listVal[n]);
var newField = ChampSpecial(curData,realIdx,fctSpe);
if(newField!=''){replacements.push(newField+'∏'+'#ch'+(n+1)+'#')};
//Logger.log('value in '+n+'='+realIdx+' >> '+Headers[realIdx]+' = '+ChampSpecial(curData,realIdx,fctSpe))
app.getElementById('textField'+(n+1)).setHTML(ChampSpecial(curData,realIdx,fctSpe));
if(e.parameter.source!='dataSelection'){
body.replaceText('#ch'+(n+1)+'#',newField);
}
}
UserProperties.setProperty('replacements',replacements.join('|'));// memorize the replacement pattern
cloakOn();// hide hidden fields
return app;
}
function fieldsInDoc(e){ // this function does the reverse process and restores the field identifiers
cloakOff();// show hidden fields
var replacements = UserProperties.getProperty('replacements').split('|');
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
for(var n=0;n<replacements.length;++n){
var field = replacements[n].split('∏')[1];
var testVal = replacements[n].split('∏')[0];
body.replaceText(testVal,field);
}
}
function ChampSpecial(curData,idx,ref){ // this function handles a special case for a specific field, the relevant part is right below, see comment
if(idx==-1){return''};
if(curData[idx-1]==''){return'°vide'+idx+'°'};// this is the "empty" identifier
if(idx<ref){return curData[idx]};
if(idx>ref){return curData[idx-1]}
var firstSpace = curData[idx-1].indexOf(' ');
var apos = curData[idx-1].indexOf("'");
//Logger.log('firstSpace='+firstSpace+' apos='+apos)
if(firstSpace<4&&firstSpace>-1){return curData[idx-1].substring(firstSpace+1)};
if(apos<3&&apos>-1){return curData[idx-1].substring(apos+1)};
return curData[idx-1];
}
EDIT : thanks to Mogsdad's brilliant answer I wrote these 2 functions to hide/show the unused fields. Sinc in my case I use °XX° (XX=2 digit number) to keep track of the unused fields I had to modify his code to look for this particular string and used 2 loops to get all the fields.
I call these function from the menu AND from the two other functions that handle the replacement (I updated the code above as well)
It might appear a waste of time since I iterate more that 100 times but the result is instantaneous... so why bother ?
here is the code in case it gives someone an idea.
function cloakOn() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var found = [];
for(var n=1;n<23;++n){
for(var f=0;f<5;++f){
if(f==0){found[f] = body.findText('°'+Utilities.formatString("%02d",n)+'°')}else{found[f] = body.findText('°'+Utilities.formatString("%02d",n)+'°',found[f-1])}
if(found[f]!=null){
var elemTxt = found[f].getElement().asText();
elemTxt.setFontSize(found[f].getStartOffset(), found[f].getEndOffsetInclusive(),0)
var background = elemTxt.getBackgroundColor(found[f].getStartOffset()) || "#ffffff";
elemTxt.setForegroundColor(found[f].getStartOffset(), found[f].getEndOffsetInclusive(), background);
}
}
}
}
function cloakOff() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var found = [];
for(var n=1;n<23;++n){
for(var f=0;f<5;++f){
if(f==0){found[f] = body.findText('°'+Utilities.formatString("%02d",n)+'°')}else{found[f] = body.findText('°'+Utilities.formatString("%02d",n)+'°',found[f-1])}
if(found[f]!=null){
var elemTxt = found[f].getElement().asText();
var size = elemTxt.getParent().getFontSize();
elemTxt.setFontSize(found[f].getStartOffset(), found[f].getEndOffsetInclusive(),size)
var background = elemTxt.getBackgroundColor(found[f].getStartOffset()) || "#000000";
elemTxt.setForegroundColor(found[f].getStartOffset(), found[f].getEndOffsetInclusive(), background);
}
}
}
}
Serge, I've been working on the very same problem! I've got a partial workaround to share, and some ideas to take it further.
There is no way to embed hidden text in Google Docs, as eloquently stated by Gill on the old forum. If there was, your mailmerge would be trivial!
How about making your tags or "cookies" (almost) invisible, though? Below is a scriplet that adds a "cloaking" function to a document. It has extras as well; it queries the user for text to cloak, then searches for all instances of that text and cloaks them. The idea I settled on was to make the text as small as possible (fontsize 0) and to match the foreground color to the background color.
// in menu: .addItem('Text Cloaking', 'cloakOn')
/**
* Find all matches of target text in current document, and cloak them.
* At this time, that consists of making the text tiny, but still visible.
* This is an experiment - my hope was to find a way to implement something
* like document variables, placeholders that would not be forgotten, so
* that values could be changed, or even dynamic.
*
* #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 cloakOn(target) {
// If no search parameter was provided, ask for one
if (arguments.length == 0) {
var ui = DocumentApp.getUi();
var result = ui.prompt('Text Cloaking',
'Enter text to cloak:', ui.ButtonSet.OK_CANCEL);
// Exit if user hit Cancel.
if (result.getSelectedButton() !== ui.Button.OK) return;
// else
target = result.getResponseText();
}
var doc = DocumentApp.getActiveDocument();
var bodyElement = doc.getBody();
var searchResult = bodyElement.findText(target);
while (searchResult !== null) {
var thisElement = searchResult.getElement();
var thisElementText = thisElement.asText();
//Logger.log(url);
thisElementText.setFontSize(searchResult.getStartOffset(), searchResult.getEndOffsetInclusive(),0);
var background = thisElementText.getBackgroundColor(searchResult.getStartOffset()) || "#ffffff";
thisElementText.setForegroundColor(searchResult.getStartOffset(), searchResult.getEndOffsetInclusive(),
background);
// search for next match
searchResult = bodyElement.findText(target, searchResult);
}
}
To make use of this in the text-replacement operation, the replacement text would carry a cloaked tag (as you're doing). I think you'd want to make your tags as short as possible, so that the white space they occupy in the final document is very small - I was playing with using a series of unicode characters as digits, to give a large range of 2-digit 'numbers' that would be unlikely to show up in any other context.