Google Apps Script: How to copy text and preserve formatting? - google-apps-script

Consider the document represented by the next 3 lines.
..some text..
6 7 8 9 10 11
..some text..
Imagine that all the numbers are their respective font sizes(i.e. 10 is font size 10). Now I want to insert an inline image in the space between 8 and 9, and remove that space, but NOT destroy the formatting(sizes in this example) of the unaffected text. The result would be
..some text..
6 7 89 10 11
..some text..
However, when I try
function placeImage() {
var s = DocumentApp.getActiveDocument().getBody();
//Find in Document
var found = s.findText("8");
if(found==null)
return 0;
var foundLocation = found.getStartOffset(); //position of image insertion
//Get all the needed variables
var textAsElement = found.getElement();
var text = textAsElement.getText();
var paragraph = textAsElement.getParent();
var childIndex = paragraph.getChildIndex(textAsElement); //gets index of found text in paragraph
//Problem part - when removing the space, destroys all formatting
var textRemaining = text.substring(foundLocation + 2);
textAsElement.deleteText(foundLocation, text.length-1);
if(textRemaining != "")
paragraph.insertText(childIndex+1, textRemaining);//destroys formatting for the rest of the child index
//Insert image
var imgSource = UrlFetchApp.fetch("https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Red_information_icon_with_gradient_background.svg/48px-Red_information_icon_with_gradient_background.svg.png");
var ingBlob = imgSource.getBlob();
paragraph.getChild(childIndex+1).insertInlineImage(foundLocation, ingBlob);
}
The problem is that when I remove the space and create another child element in the paragraph to insert the image at, the substring also deletes the remaining text formatting. I have tried looking into copy(), but I'm not sure how that would work efficiently.
I have researched many other places, both answers here don't preserve formatting, and position.insertInlineImage() seems to be broken, as asked here.

It basically depends on the content of your document how it is set with paragraphs. Tried with simple content in the document and insert the image.
It is inserting the image well and also not changing the formatting of rest of the text aswell.
Check the code below:
function placeImage()
{
var s = DocumentApp.getActiveDocument().getBody();
var number = '8'
//Find in Document
var found = s.findText(number);
if(found==null)
return 0;
var foundLocation = found.getStartOffset(); //position of image insertion
//Get all the needed variables
var textAsElement = found.getElement();
var text = textAsElement.asText().copy();// getText();
textAsElement.editAsText().deleteText(foundLocation + number.length ,textAsElement.asText().getText().length -1)
text.asText().editAsText().deleteText(0, foundLocation + 1 );
//Insert image
var imgSource = UrlFetchApp.fetch('https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Red_information_icon_with_gradient_background.svg/48px-Red_information_icon_with_gradient_background.svg.png');
var ingBlob = imgSource.getBlob();
var children =DocumentApp.getActiveDocument().getBody().getChild(0).asParagraph().appendInlineImage(ingBlob);
DocumentApp.getActiveDocument().getBody().getChild(0).asParagraph().appendText(text);
var child = DocumentApp.getActiveDocument().getBody().getNumChildren();
}
The document content tested is:
6 7 8 9 10 11
(The size of the text is similar to as you mentioned. '8' is size 8 and so on)
You have to test using trial and error method as per your document content.
Hope that helps!

Related

appendParagraph - Adding Blank Space on the First Line

I am new to Google Apps Script and trying to generate a document using Excel Data.
I am able to successfully create the document and add tables and paragraphs.
I see some odd behavior with appendParagraph.
It adds a space (blank like) when adding the first paragraph. The the paragraphs that follow are fine.
I tried replacing the new line (\n) with '', but did not work.
Any suggestion how to get rid of the line or add a paragraph without the blank line for the first paragraph (Sample Code below).
titleStyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] = DocumentApp.HorizontalAlignment.CENTER
titleStyle[DocumentApp.Attribute.FONT_SIZE] = 19
titleStyle[DocumentApp.Attribute.FONT_FAMILY] = 'Arial'
titleStyle[DocumentApp.Attribute.BOLD] = true
const compTitle = cell.appendParagraph('TITLE PARAGRAPH')
compTitle.setAttributes(titleStyle)
const contentStyle = {}
contentStyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] = DocumentApp.HorizontalAlignment.CENTER
contentStyle[DocumentApp.Attribute.FONT_SIZE] = 11
contentStyle[DocumentApp.Attribute.FONT_FAMILY] = 'Arial'
contentStyle[DocumentApp.Attribute.BOLD] = false
const content = cell.appendParagraph('CONTENT PARAGRAPH')
content.setAttributes(contentStyle)
As Cooper mentioned, your documents start off with a blank paragraph. If you want to remove it programmatically, run the following function:
function removeEmptyPara() {
const paras = DocumentApp.getActiveDocument().getBody().getParagraphs();
const firstPara = paras[0];
if (paras.length > 1 && !firstPara.getText()) firstPara.removeFromParent()
}
Let me know if this helps.

How to set a certain number of spaces or indents before a Paragraph in Google Docs using Google Apps Script

I have a 20 line script, and I want to make sure that each paragraph is indented exactly once.
function myFunction() {
/*
This function turns the document's format into standard MLA.
*/
var body = DocumentApp.getActiveDocument().getBody();
body.setFontSize(12); // Set the font size of the contents of the documents to 9
body.setForegroundColor('#000000');
body.setFontFamily("Times New Roman");
// Loops through paragraphs in body and sets each to double spaced
var paragraphs = body.getParagraphs();
for (var i = 3; i < paragraphs.length; i++) { // Starts at 3 to exclude first 4 developer-made paragraphs
var paragraph = paragraphs[i];
paragraph.setLineSpacing(2);
// Left align the first cell.
paragraph.setAlignment(DocumentApp.HorizontalAlignment.LEFT);
// One indent
paragraph.editAsText().insertText(0, "\t"); // Adds one tab every time
}
var bodyText = body.editAsText();
bodyText.insertText(0, 'February 3, 1976\nMrs. Smith\nYour Name Here\nSocial Studies\n');
bodyText.setBold(false);
}
The code I have tried doesn't work. But my expected results are that for every paragraph in the for loop in myFunction(), there are exactly 4 spaces before the first word in each paragraph.
Here is a sample: https://docs.google.com/document/d/1sMztzhOehzheRdqumC6PLnvk4qJgUCSE0irjTZ0FjTQ/edit?usp=sharing
If the user uses Autoformat, but already has the paragraphs indented...
Update
I have investigated use of the Paragraph.setIndentFirstLine() method. When I set it to four, it sets it to 1 space. Now I realize this is because points and spaces are not the same thing. What number do I need to multiply by to get four spaces in points?
Let us consider a few basic identing operations: manual and by script.
The following image shows how to indent current paragraph (cursor stays inside this one).
Please note, the units are centimetres. Also note, that the paragraph does not include leading spaces or tabs, we have no need of them.
Suppose we would like to get the indent values in the script and apply them to the next paragraph. Look at the code below:
function myFunction() {
var ps = DocumentApp.getActiveDocument().getBody().getParagraphs();
// We work with the 5-th and 6-th paragraphs indeed
var iFirst = ps[5].getIndentFirstLine();
var iStart = ps[5].getIndentStart();
var iEnd = ps[5].getIndentEnd();
Logger.log([iFirst, iStart, iEnd]);
ps[6].setIndentFirstLine(iFirst);
ps[6].setIndentStart(iStart);
ps[6].setIndentEnd(iEnd);
}
If you run and look at the log, you will see something like this: [92.69291338582678, 64.34645669291339, 14.173228346456694]. No surprise, we have typographic points instead of centimetres. (1cm=28.3465pt) So we can measure and modify any paragraph indent values precisely.
Addition
For some reasons you might want to control spaces number at the beginning of the paragraph. It is also possible by scripting, but it has no effect on the paragraph's "left" or "right" indents.
Sample code below is for similar task: count leading spaces number of the 5-th paragraph and make the same number of spaces at the beginning of the next one.
function mySpaces() {
var ps = DocumentApp.getActiveDocument().getBody().getParagraphs();
// We work with the 5-th and 6-th paragraphs indeed
var spacesCount = getLeadingSpacesCount(ps[5]);
Logger.log(spacesCount);
var diff = getLeadingSpacesCount(ps[6]) - spacesCount;
if (diff > 0) {
ps[6].editAsText().deleteText(0, diff - 1);
} else if (diff < 0) {
var s = Array(1 - diff).join(' ');
ps[6].editAsText().insertText(0, s);
}
}
function getLeadingSpacesCount(p) {
var found = p.findText("^ +");
return found ? found.getEndOffsetInclusive() + 1 : 0;
}
We have used methods deleteText() and insertText() of the class Text for proper corrections and findText() to locate the spaces if any. Note, the last method argument is a string, representing a regular expression. It matches "all leading spaces", if they exist. See more details about regular expression syntax.

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 )

Google Documents: set heading as defined in current document

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.

How to create a pagination photos?

I'd like to integrate a pagination in photo gallery project.
Ex: << previous 1 2 3 next >>
Let's say I have 13 photos and want to display on each page first 6 photos. So in total, I must have 3 pages of 6 photos each and each page number is clickable to display the maximum of 6 photos...
How would I proceed the right method?
Here's what I though:
var totalPhotos:uint;
var maxNumberThumbPerPage:uint = 6;
var totalPage:uint;
totalPhotos = tabPhoto.length;
totalPage = Math.ceil(totalPhotos/maxNumberThumbPerPage);
create a function that goes something like this
var imagesArray:Array = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
function createPage($pageNum:int, $perPage:int = 6):Array{ // though vector is preferred
// imagesArray - the array holdig all the images
var iStart:int = $pageNum * $perPage;
var iEnd:int = ($pageNum + 1) * $perPage;
if (iEnd > imagesArray.length) { iEnd = imagesArray.length}
return imagesArray.slice(iStart, iEnd);
}
trace( createPage(0));
trace( createPage(1));
trace( createPage(2));
this will get you the content of each page, this is one of the trickier parts, but as you can see still pretty simple.
other part would be to create the navigation and create the rendering part