Insert table into google document at cursor position

I am working on adding Google Docs, which turns text into a table. It allows the user to either 1) select any text, or 2) place their cursor in the desired text, and when you click on the user button in the sidebar, the script will:

  • Insert one row, a table with one cell at the cursor position or startIndex of the selected text,
  • Place inside the cell either the text selected by the user or the full text of the element in which the cursor is placed,
  • Delete the original selected text / element so that only the table remains

(Essentially, it's just “drawing” a table around the selected text or element)

A review of the documentation shows that text can be inserted through var element = cursor.insertText('ಠ‿ಠ'); , but there is no similar method for inserting other elements, Thus, I am trying to use insertTable(childIndex, cells) with varying degrees of success. By focusing on the circumstance in which the user simply places the cursor inside the element, I can insert the table with the correct text and delete the source text, but I cannot find a way to insert the table at the desired position. I tried the following, to no avail:

  • var newTable = body.insertTable(cursor.getOffset(), [['Test Text']]); - creates a table, but inserts it into the wrong position, apparently based on where the cursor is located in the text, usually at the beginning of the document.

  • var el = cursor.getElement().asBody(); var newTable = el.insertTable(0, [['Test Text']]); - doing nothing

  • var el = cursor.getElement().getParent().asBody(); var newTable = el.insertTable(0, [['Test Text']]); - doing nothing

  • var el = cursor.getElement(); var parent = el.getParent().getChildIndex(el); var newTable = body.insertTable(parent, [['Test Text']]); - Inserts a table at the very beginning of the document.

The search gives this very similar question with the suggestion to insert some placeholder text at the cursor position, and then search for the document for this inserted row and place the table. I have two main problems with this approach:

  • I need to change the text directly around the insertion point, and adding extra text seems like this can make it more difficult, and
  • More importantly, I still don't know how to get the right position for the insertion point of the table.

If someone can extend the placeholder-text method or can provide a new solution, this will be very helpful. Thanks!

+5
source share
2 answers

I used the following GAS code to insert a table at the current cursor position in Google Doc. It works better if your cursor is on a new line.

  var doc = DocumentApp.getActiveDocument(); var body = doc.getBody(); var cursor = doc.getCursor(); var element = cursor.getElement(); var parent = element.getParent(); //here table is an array of cells like // [[r1c1, r1c2], [r2c1, r2c2]] body.insertTable(parent.getChildIndex(element) + 1, table); 

Edit: To better understand how the cursor position works in the Google Doc, I recommend starting the next github cursor pointer project

https://github.com/google/google-apps-script-samples/tree/master/cursor_inspector

+3
source

TL DR: use the code snippet below.

It seems that what Google Docs does when choosing Insert> Table is to split the current paragraph into two paragraphs and then insert a new table between them.

The hard part is to split the paragraph into two. I tried to achieve this in several ways. I could not find the API request from the Google Apps Script documentation that does this (the Spreadsheet API has a moveTo() method for the Range object, but that cannot help us here). I was hoping that I could copy the elements from this position in the paragraph further to another paragraph and delete the originals. However, since the Document Service does not allow the explicit insertion of several types of elements , but can only be managed locally, for example, Equation elements, copying these one by one is not possible.

Fortunately, Paragraph has a copy() method that performs a deep copy. Therefore, I take care to copy the entire paragraph, delete everything from the cursor position forward in the original paragraph, and delete everything to where the cursor was in the copy of the paragraph. This way you split the paragraph in the middle and you can insert the table in the desired position. It works the same for ListItem .

Here is the code for the splitParagraphAt() function that returns the newly created paragraph (or list item, which is the next native original). I did a few extra checks in the code to make sure that it does what you think it does. After that, I added a short code excerpt on how this can be used to insert a table at the current cursor position. You can use splitParagraphAt() same way as inserting any element at the cursor position. I have not tested this completely, so any input is welcome.

 /** * Splits the contents of the paragraph (or list item) at the given position, * producing two adjacent paragraphs (or list items). This function may be used * to insert any kind of element at an arbitrary document position, but placing * it immediately before the second paragraph (or list item). * * @param {Position} pos The position where the paragraph (or list item) should * be split. `pos.getElement()` should be either a Text, Paragraph or * ListItem object. * * @returns {ContainerElement} The second (newly created) Paragraph or ListItem * object. * */ function splitParagraphAt(pos) { var el = pos.getElement(), offset = pos.getOffset(); var inParagraph = (el.getType() == DocumentApp.ElementType.PARAGRAPH || el.getType() == DocumentApp.ElementType.LIST_ITEM); if (!inParagraph && (el.getType() != DocumentApp.ElementType.TEXT)) { throw new Error("Position must be inside text or paragraph."); } var par; if (inParagraph) { // in this case, `offset` is the number of child elements before this // Position within the same container element par = el; if (offset == par.getNumChildren()) { // we're at the end of the paragraph return par.getParent().insertParagraph( par.getParent().getChildIndex(par) + 1, ""); } el = par.getChild(offset); } else { par = el.getParent(); if (par == null || (par.getType() != DocumentApp.ElementType.PARAGRAPH && par.getType() != DocumentApp.ElementType.LIST_ITEM)) { throw new Error("Parent of text is not a paragraph or a list item."); } } var parContainer = par.getParent(); if (!("insertParagraph" in parContainer)) { throw new Error("Cannot insert another paragraph in this container."); } // This assumes the given position is in the current document. // alternatively, one may traverse through parents of par until document // root is reached. var doc = DocumentApp.getActiveDocument(); var elIndex = par.getChildIndex(el); var newPar = par.copy(); var newEl = newPar.getChild(elIndex); // remove everything up to position from the new element if (!inParagraph && (offset != 0)) { newEl.deleteText(0, offset-1); } newEl = newEl.getPreviousSibling(); while (newEl != null) { // get the previous sibling before we remove the element. var prevEl = newEl.getPreviousSibling(); newEl.removeFromParent(); newEl = prevEl; } // since we might remove el itself, we get the next sibling here already var nextEl = el.getNextSibling(); // remove everything from position onwards in the original element if (!inParagraph && (offset != 0)) { el.deleteText(offset, el.getText().length-1); } else { // we're at the beginning of the text (or just before a paragraph // subelement) and need to remove the entire text/subelement. el.removeFromParent(); } el = nextEl; while (el != null) { // get the next sibling before we remove the element. nextEl = el.getNextSibling(); el.removeFromParent(); el = nextEl; } // actually insert the newly created paragraph into the document tree. switch (par.getType()) { case DocumentApp.ElementType.PARAGRAPH: parContainer.insertParagraph(parContainer.getChildIndex(par)+1, newPar); break; case DocumentApp.ElementType.LIST_ITEM: parContainer.insertListItem(parContainer.getChildIndex(par)+1, newPar); break; } return newPar; } 

Here is the code excerpt for inserting the table at the cursor position and setting the cursor position in the first cell of the table:

 var doc = DocumentApp.getActiveDocument(); var cursor = doc.getCursor(); var el = (cursor.getOffset() == 0? cursor.getElement() : splitParagraphAt(cursor)); var parentEl = el.getParent(); var table = parentEl.insertTable(parentEl.getChildIndex(el), [['ಠ‿ಠ']]); doc.setCursor(doc.newPosition(table.getCell(0, 0), 0)); 

Please note that additional checks are still needed to see if there is a choice or not, etc. In particular, this accepted cursor will not be null .

+1
source

Source: https://habr.com/ru/post/1236923/


All Articles