1

I have a Google App Script that merges data values from rows in a sheet with a template doc. The template doc paragraphs have inline images. When I remove the images, the script works perfectly. When I add the images back, I get the following error: Service unavailable: Documents (line 34, file "Code")

Any ideas on what I need to do? FYI: Line 34 is noted with a comment below...

function buildGivingStatements() {
  var tempDocID = "1-SAQYP41wwnah6tF15nvT8fSx1rjCHN57T7lECFTCCw";
  var finDocID = "1TnR63Yq9oiUwxxJ5MTHbm-OX3pqvH6iW7bBLcFC2v1w";
  var sheetID = "19Sl61Rp37bPWc_fsCqpu59DFsDdwZF4zxdjHlplA-fI";

  var tempDoc = DocumentApp.openById(tempDocID);
  var finDoc = DocumentApp.openById(finDocID);
  var sheet = SpreadsheetApp.openById(sheetID).getSheetByName("Annual Report");

  var tDATE = Utilities.formatDate(new Date(), "America/Chicago", "EEEE, MMMM dd, yyyy");
  var tYEAR = Utilities.formatDate(new Date(), "America/Chicago", "yyyy");

  var data = sheet.getRange(6,1,1,2).getDisplayValues();
  //var data = sheet.getRange(6,1,sheet.getLastRow()-1,4).getValues();

  var tempPs = tempDoc.getBody().getParagraphs();

  finDoc.getBody().clear();

  data.forEach(function(r){
    replaceTempVars(r[0], r[1], tDATE, tYEAR, finDoc, tempPs);
  });

}



function replaceTempVars(NAME, AMOUNT, tDATE, tYEAR, finDoc, tempPs) {
  tempPs.forEach(function(p){
    var elType = p.getType();
    Logger.log(elType);

    if(elType == "PARAGRAPH") {
      finDoc.getBody().appendParagraph( //LINE 34
        p
          .copy()
          .replaceText("{{NAME}}", NAME)
          .replaceText("{{AMOUNT}}", AMOUNT)
          .replaceText("{{DATE}}", tDATE)
          .replaceText("{{YEAR}}", tYEAR)
      );
    }else if(elType == "LIST_ITEM") {
      finDoc.getBody().appendListItem(
        p
          .copy()
      ).setGlyphType(DocumentApp.GlyphType.BULLET);
    }
  });

  finDoc.getBody().appendPageBreak();
}
James Cook
  • 13
  • 4
  • Maybe you can search for `{{NAME}}` and if it doesn't exist move to the next paragraph. – Cooper Jan 14 '20 at 19:58
  • 1
    Can you provide the sample template Document for replicating your issue? Of course, please remove your personal information. – Tanaike Jan 14 '20 at 23:19
  • Link to template doc has been added – James Cook Jan 15 '20 at 14:21
  • Thank you for replying and sharing the sample template Document. I could understand about the reason of your issue from the sample. So I proposed a modified script as an answer. Could you please confirm it? If I misunderstood your question and that was not the direction you want, I apologize. – Tanaike Jan 16 '20 at 03:20
  • Did my answer show you the result what you want? Would you please tell me about it? That is also useful for me to study. If this works, other people who have the same issue with you can also base your question as a question which can be solved. If you have issues for my answer yet, I apologize. At that time, can I ask you about your current situation? I would like to study to solve your issues. – Tanaike Jan 17 '20 at 23:41
  • yes it did! Apologies for not getting back to this thread sooner. Thank you for your help! – James Cook Jan 18 '20 at 14:51
  • Thank you for replying. I'm glad your issue was resolved. Thank you, too. – Tanaike Jan 18 '20 at 23:34

1 Answers1

2
  • You want to merge the template Document to the existing Google Document by replacing the texts.
  • In your current issue, an error of Service unavailable occurs at the script of finDoc.getBody().appendParagraph().
    • When the image at the top of Document is deleted, the script works.
  • You want to achieve this using Google Apps Script.

If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.

Modification points:

  • From your shared Google Document, I could know that your template Document uses a positioned image. I think that the reason of the error of Service unavailable is due to the positioned image. When the paragraph including positioned image is copied, such error occurs.
    • In order to avoid this issue, I would like to propose to modify your script by separating the positioned image and paragraph.
    • For this, I used getNumChildren() instead of getParagraphs(). Because I thought that it is required to use insert method instead of append method. Because when append method is used, the empty paragraph is created at the top paragraph.

Modified script:

Before you run the script, please enable Google Docs API at Advanced Google services.

function buildGivingStatements() {
  var tempDocID = "1-SAQYP41wwnah6tF15nvT8fSx1rjCHN57T7lECFTCCw";
  var finDocID = "1TnR63Yq9oiUwxxJ5MTHbm-OX3pqvH6iW7bBLcFC2v1w";
  var sheetID = "19Sl61Rp37bPWc_fsCqpu59DFsDdwZF4zxdjHlplA-fI";

  var tempDoc = DocumentApp.openById(tempDocID);
  var finDoc = DocumentApp.openById(finDocID);
  var sheet = SpreadsheetApp.openById(sheetID).getSheetByName("Annual Report");
  var tDATE = Utilities.formatDate(new Date(), "America/Chicago", "EEEE, MMMM dd, yyyy");
  var tYEAR = Utilities.formatDate(new Date(), "America/Chicago", "yyyy");
  var data = sheet.getRange(6,1,1,2).getDisplayValues();

  // I modified below script.
  var finBody = finDoc.getBody();
  finBody.clear();
  var tempBody = tempDoc.getBody();
  var tempNumChildren = tempBody.getNumChildren();
  var pos = 0;
  data.forEach(function(r){
    replaceTempVars(r[0], r[1], tDATE, tYEAR, finBody, tempBody, tempNumChildren, pos);
  });

  // If you want to delete the last empty page, please use the following script. In this case, please enable Docs API at Advanced Google services.
  finDoc.saveAndClose();
  var c = Docs.Documents.get(finDocID, {fields: "body.content"}).body.content.pop();
  Docs.Documents.batchUpdate({requests: [{deleteContentRange: {range: {startIndex: c.startIndex - 1, endIndex: c.endIndex - 1}}}]}, finDocID);
}

function replaceTempVars(NAME, AMOUNT, tDATE, tYEAR, finBody, tempBody, tempNumChildren, pos) {
  for (var i = 0; i < tempNumChildren; i++) {
    var element = tempBody.getChild(i).copy();
    var type = element.getType();
    if (type == DocumentApp.ElementType.PARAGRAPH) {
      var copyPara = element.asParagraph();
      var pImgs = copyPara.getPositionedImages();
      if (pImgs.length > 0) {
        var img = pImgs[0];
        var obj = {id: img.getId(), blob: img.getBlob(), leftOffset: img.getLeftOffset(), topOffset: img.getTopOffset(), width: img.getWidth(), height: img.getHeight(), layout: img.getLayout()};
        copyPara.removePositionedImage(obj.id);
        var texts = copyPara.replaceText("{{NAME}}", NAME).replaceText("{{AMOUNT}}", AMOUNT).replaceText("{{DATE}}", tDATE).replaceText("{{YEAR}}", tYEAR);
        var paragraph = finBody.insertParagraph(pos + i, texts);
        paragraph.addPositionedImage(obj.blob).setWidth(obj.width).setHeight(obj.height).setLayout(obj.layout).setLeftOffset(obj.leftOffset).setTopOffset(obj.topOffset);
      } else {
        var texts = element.asParagraph().replaceText("{{NAME}}", NAME).replaceText("{{AMOUNT}}", AMOUNT).replaceText("{{DATE}}", tDATE).replaceText("{{YEAR}}", tYEAR);
        finBody.insertParagraph(pos + i, texts);
      }
    } else if (type == DocumentApp.ElementType.LIST_ITEM) {
      var glyphType = element.asListItem().getGlyphType();
      finBody.insertListItem(pos + i, element).setGlyphType(glyphType);
    } else if (type == DocumentApp.ElementType.INLINE_IMAGE) {
      finBody.insertImage(pos + i, element);
    }
  }
  pos += tempNumChildren;
}

Note:

  • In this modified script has been prepared by testing your template Document. If you want to use other template, an error might occur. So please be careful this.
  • About the Google Document of finDocID, please use the Document which has the same margins of Document page with the template Document. If the default Google Document is used, the result might not be what you expect. Please be careful this.

References:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • Thanks for this answer. You've identified the correct root cause of "Service Unavailable: Documents" exceptions when trying to copy paragraphs containing positioned images. I used your answer to address a similar problem. – jamesmulcahy Jul 16 '21 at 16:18