I'm writing a function that will duplicate the current document by moving every element into a new document -- it's part of a larger project.
However, when there is a footnote in the document, the function fails, with this error.
Service Documents failed while accessing document with id [id of target doc]
Now, I have no problem with the function not being able to transfer the footnote into a new document -- or at least transferring just the text of the footnote. However, this error stops my entire function from running (none of the elements in the doc get transferred over).
Is it possible to ignore footnotes or convert them to text, and allow the function to transfer all other elements into the new document?
Here's a sample document with a footnote
Here's my code:
function duplicateDocument() {
var currentDoc = DocumentApp.getActiveDocument().getBody();
var targetDoc = DocumentApp.create('New Doc');
var totalElements = currentDoc.getNumChildren();
//Goes through each type of element to preserve formatting
for( var index = 0; index < totalElements; ++index ) {
var body = targetDoc.getBody();
var element = currentDoc.getChild(index).copy();
var type = element.getType();
if( type == DocumentApp.ElementType.PARAGRAPH ){
body.appendParagraph(element);
}
else if( type == DocumentApp.ElementType.TABLE){
body.appendTable(element);
}
else if( type == DocumentApp.ElementType.LIST_ITEM){
body.appendListItem(element);
} else if( type == DocumentApp.ElementType.BOOKMARK ){
body.appendBookmark(element);
} else if( type == DocumentApp.ElementType.INLINE_IMAGE ){
body.appendImage(element);
} else if( type == DocumentApp.ElementType.HORIZONTAL_RULE ){
body.appendHorizontalRule();
} else if( type == DocumentApp.ElementType.PAGE_BREAK ){
body.appendPageBreak();
}
}
}
At current stage it's unfortunately not possible to create footnotes programmatically with Apps Script
A related feature request already exists, but it has not been implemented yet
This must be the reason tht copying a paragraph with foot notes (so trying to create a footnote in the new document) results in buggy behavour
This issue seems also to be still in investigation
In the mean time - if we can live with copying the document without the footnotes - the best workaround would be to remove the footnotes programmatically
This you can do by looping through all the children of a paragraph, and if you encounter a footnote - remove it
Sample:
if( type == DocumentApp.ElementType.PARAGRAPH){
var num = element.asParagraph().getNumChildren();
for( var i = 0; i < num; i++ ) {
var child = element.asParagraph().getChild(i);
var childType = child.getType();
if( childType == DocumentApp.ElementType.FOOTNOTE){
child.removeFromParent();
}
}
body.appendParagraph(element);
}
Related
I've successfully used this code to copy the entirety of one doc into another doc:
const newestFile = DocumentApp.openById("ID").getBody();
const archive = DocumentApp.openById("ID").getBody();
let index = 12;
let el, type;
for (let i = 0; i < newestFile.getNumChildren(); i++){
el = newestFile.getChild(i);
type = el.getType();
switch (type){
case DocumentApp.ElementType.PARAGRAPH:
archive.insertParagraph(index,el.copy());
index++;
break;
case DocumentApp.ElementType.LIST_ITEM:
archive.insertListItem(index,el.copy());
index++;
break;
case DocumentApp.ElementType.TABLE:
archive.insertTable(index,el.copy());
index++;
break;
}
}
However, I now need to copy a portion of a doc into another doc, and I can't figure it out. If I knew how to get the body index of any element I could do it the same way, but I don't know if that's even possible. The text I need to copy out will always be preceded by a specific text ("Current Week") and end immediatly before a specific text ("ARCHIVE").
Description
Here is a simple example of how to copy between certain text. I've only covered paragraphs and tables but any other type of Element can be handled.
Test Document
Script
function myFunction() {
try {
let doc = DocumentApp.getActiveDocument();
let body = doc.getBody();
let count = body.getNumChildren();
doc = DocumentApp.create("dummy");
let copy = doc.getBody();
let start = false;
for( let i=0; i<count; i++ ) {
let child = body.getChild(i);
if( child.getType() == DocumentApp.ElementType.PARAGRAPH ) {
if( child.asParagraph().findText("Current Week") ) start = true;
if( start ) copy.appendParagraph(child.asParagraph().copy());
if( child.asParagraph().findText("ARCHIVE") ) break;
}
else if( child.getType() == DocumentApp.ElementType.TABLE ) {
if( start ) copy.appendTable(child.asTable().copy());
}
}
}
catch(err) {
console.log("Error in myFunction - "+err)
}
}
Reference
https://developers.google.com/apps-script/reference/document/body#getChild(Integer)
https://developers.google.com/apps-script/reference/document/element-type
https://developers.google.com/apps-script/reference/document/body
I'm creating a function that will transfer text that a user is currently selecting from one document to another.
It works fine most of the time, but when I try to transfer list items, as seen on this image, the list item numbers disappear.
After being transferred to the target document, the list items from the last image now look like this.
Is there a way to make sure the numbers get transferred, or at least recreate them in an efficient way?
Thanks!
Minimal Reproducible Example:
function sendToDoc() {
var currentDoc = DocumentApp.getActiveDocument().getSelection();
var targetDoc = DocumentApp.openByUrl(a different documents url);
var body = targetDoc.getBody();
//if you are selecting some text, this function will transfer your selection into the target document
if(currentDoc) {
var totalElements = currentDoc.getRangeElements();
//for each element in your selection
for( var index = 0; index < totalElements.length; ++index ) {
var element = totalElements[index].getElement().copy();
var type = element.getType();
//gets the element type and transfers it over to the target doc
if( type == DocumentApp.ElementType.PARAGRAPH ){
body.appendParagraph(element);
}
else if( type == DocumentApp.ElementType.TABLE){
body.appendTable(element);
}
else if( type == DocumentApp.ElementType.LIST_ITEM){ //this is where the list items get transferred
body.appendListItem(element);
}
else if( type == DocumentApp.ElementType.INLINE_IMAGE ){
body.appendImage(element);
}
else if( type == DocumentApp.ElementType.HORIZONTAL_RULE ){
body.appendHorizontalRule();
}
else {
}
}
}
Set GlyphType to NUMBER:
body
.appendListItem(element)
.setGlyphType(DocumentApp.GlyphType.NUMBER)
Due the answer on this topic I can copy content form template doc to another saving all formatting.
But when I add some pictures to couple of paragraphs the result document shows pictures as picture logo
The question is how to copy images right on this way?
You need to adapt your insert method to every different element type...
an example could be like this :
function copyElementsByType() {
var sourceDoc = DocumentApp.openById('yyyy');
var targetDoc = DocumentApp.openById('xxxx');;
var bodyS = sourceDoc.getBody();
var otherBody = targetDoc.getBody()();
var elements = bodyS.getNumChildren()
for( var e=0;e<elements;e++) {
var element = otherBody.getChild(e).copy();
var type = element.getType();
if( type == DocumentApp.ElementType.PARAGRAPH ){
body.insertParagraph(e,element)}
else if( type == DocumentApp.ElementType.TABLE ){
body.insertTable(e,element)}
else if( type == DocumentApp.ElementType.LIST_ITEM ){
body.insertListItem(e,element)}
else if( type == DocumentApp.ElementType.INLINE_IMAGE ){
body.insertImage(e,element)
} else {
throw new Error("check what to do with this type of element : "+type);
}
}
}
Due to this answer now I can make copy of elements with images right, but I spotted one more thing - if image inserted as 'in text' - then copying is done well. But when I make it 'text wrap', I can't find this element at all!
Here is the code of test:
function test_show_all_structure_of_doc() {
var final = 'final';
var doc = get_doc(working_directory, final);
var body = doc.getBody();
var elements = body.getNumChildren();
for( var i=0;i<elements;i++) {
var element = body.getChild(i).copy();
var type = element.getType();
if( type == DocumentApp.ElementType.PARAGRAPH ){
Logger.log('paragraph' + i);
var children = element.getNumChildren();
if (children >0) {
for (var j=0;j<children;j++) {
var subelement = element.getChild(j).copy();
var subtype = subelement.getType();
Logger.log('subelement ' + j + ":" + subtype);
if (subtype == DocumentApp.ElementType.TEXT) Logger.log(subelement.getText());
}
}
}
else if( type == DocumentApp.ElementType.TABLE ){
Logger.log('table');}
else if( type == DocumentApp.ElementType.LIST_ITEM ){
Logger.log('list item');}
else if( type == DocumentApp.ElementType.INLINE_IMAGE ){
Logger.log('inline image');}
else {
throw new Error("check what to do with this type of element : "+ type);
}
}
}
so where I can find textwrap image? Or it is impossible for now?
This is not possible using Google Apps Script (for now) and is the object of an enhancement request for more than 2 years now.
In the mean time, only inline image is supported.
My goal is to replace a piece of text in a Google Drive document with the contents of another document.
I have been able to insert the document at a certain position in the other document, but I'm having trouble determining the child index of the piece of text I want to replace. Here is what I have so far:
function replace(docId, requirementsId) {
var body = DocumentApp.openById(docId).getActiveSection();
var searchResult = body.findText("<<requirementsBody>>");
var pos = searchResult.?? // Here I would need to determine the position of the searchResult, to use it in the insertParagraph function below
var otherBody = DocumentApp.openById(requirementsId).getActiveSection();
var totalElements = otherBody.getNumChildren();
for( var j = 0; j < totalElements; ++j ) {
var element = otherBody.getChild(j).copy();
var type = element.getType();
if( type == DocumentApp.ElementType.PARAGRAPH ) {
body.insertParagraph(pos,element);
} else if( type == DocumentApp.ElementType.TABLE ) {
body.insertTable(pos,element);
} else if( type == DocumentApp.ElementType.LIST_ITEM ) {
body.insertListItem(pos,element);
} else {
throw new Error("According to the doc this type couldn't appear in the body: "+type);
}
}
};
Any assistance would be greatly appreciated.
findText()
returns a RangeElement.
You can use
var r = rangeElement.getElement()
to get the element containing the found text.
To get its childIndex you can use
r.getParent().getChildIndex(r)
Thanks to bruce's answer I was able to figure out a solution to this problem, however if I was inserting Elements from another document I needed to actually find the index of the parent of the found text, as the found text was just a Text element inside of a Paragraph Element. So, I needed to find the index of the Paragraph Element, and then insert the new elements in relation to that Paragraph.
The code looks like this:
var foundTag = body.findText(searchPattern);
if (foundTag != null) {
var tagElement = foundTag.getElement();
var parent = tagElement.getParent();
var insertPoint = parent.getParent().getChildIndex(parent);
var otherBody = DocumentApp.openById(requirementsId).getActiveSection();
var totalElements = otherBody.getNumChildren();
for( var j = 0; j < totalElements; ++j ) {
... then same insertCode from the question above ...
insertPoint++;
}