Search code examples
google-apps-scriptmergegoogle-drive-apigoogle-docsgoogle-docs-api

How to merge multiple Google docs into one file using Google Apps Script


I have the same format Google documents in the Google Drive folder that I want to combine/merge into one Google Doc file, the sample of the Google docs to be merged looks like this. Here is the sample code that I am trying to use for this purpose:

function mergeGoogleDocs() {

  var folderId = "####"; //Where all Google docs currently are
  var folder = DriveApp.getFolderById(folderId);
  var docIDs = [];
  var combinedDoc = DocumentApp.create("Merged Document");
 
  var files = folder.getFiles();

  //Get all file Ids present in a folder
  while (files.hasNext()) {
    var file = files.next();
    docIDs.push(file.getId());
  }

  var baseDoc = DocumentApp.openById(combinedDoc.getId());

  var body = baseDoc.getActiveSection();

  for (var i = 1; i < docIDs.length; ++i) {
    var otherBody = DocumentApp.openById(docIDs[i]).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.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.INLINE_IMAGE) {
        var image = element.asInlineImage();
        var blob = image.getBlob();
        var imageFile = folder.createFile(blob);
        combinedDoc.getBody().appendImage(imageFile.getBlob());
        }
      else throw new Error('Unknown element type: ' + type);
    }
  }
}

However, when I try to run it, it shows the following error:

Exception: Service unavailable: Documents

I have also added Docs under Services on the left side in the script editor. But the issue is not resolved. Can you please guide me what is the issue with the script and why it is not able to combine all docs into one? Any help would be much appreciated.


Solution

  • Modification points:

    • When I saw your provided Document, it seems that the inline image is not used. But, the positioned image is used. In this case, when the paragraph including the positioned image is copied, such an error occurs. I thought that this might be the reason for your current issue.
    • In order to retrieve only Google Document, I would like to propose using getFilesByType(MimeType.GOOGLE_DOCS).
    • On April 15, 2013, the name of Document.getActiveSection() has been changed to Document.getBody(). Ref In the current stage, it seems that getBody() and getActiveSection() return the same DocumentBodySection object. But, I would like to recommend using getBody now.

    When these points are reflected in your showing script, it becomes as follows.

    Modified script:

    function mergeGoogleDocs() {
      var folderId = "###"; //Where all Google docs currently are
      var folder = DriveApp.getFolderById(folderId);
      var docIDs = [];
      var files = folder.getFilesByType(MimeType.GOOGLE_DOCS);
    
      //Get all file Ids present in a folder
      while (files.hasNext()) {
        var file = files.next();
        docIDs.push(file.getId());
      }
    
      if (docIDs.length == 0) return;
      var baseDoc = DocumentApp.create("Merged Document");
      var baseDocId = baseDoc.getId();
      var body = baseDoc.getBody();
    
      for (var i = 0; i < docIDs.length; ++i) {
        var otherBody = DocumentApp.openById(docIDs[i]).getBody();
        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) {
            var cp = element.asParagraph();
            var positionedImages = cp.getPositionedImages();
            if (positionedImages.length > 0) {
              var img = positionedImages[0];
              var { id, blob, width, height, layout, leftOffset, topOffset } = { id: img.getId(), blob: img.getBlob(), width: img.getWidth(), height: img.getHeight(), layout: img.getLayout(), leftOffset: img.getLeftOffset(), topOffset: img.getTopOffset() };
              cp.removePositionedImage(id);
              var p = body.appendParagraph(cp);
              p.addPositionedImage(blob)
                .setWidth(width)
                .setHeight(height)
                .setLayout(layout)
                .setLeftOffset(leftOffset)
                .setTopOffset(topOffset);
            } else {
              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.INLINE_IMAGE) {
            var image = element.asInlineImage();
            var blob = image.getBlob();
            var imageFile = folder.createFile(blob);
            combinedDoc.getBody().appendImage(imageFile.getBlob());
          } else {
            throw new Error('Unknown element type: ' + type);
          }
        }
    
        // Below script is used for removing the duplicated image. But, this might not be required to be used.
        baseDoc.saveAndClose();
        baseDoc = DocumentApp.openById(baseDocId);
        body = baseDoc.getBody();
        var lastP = body.getParagraphs().pop();
        var lastPImage = lastP.getPositionedImages();
        if (lastPImage.length != 0) {
          lastP.removePositionedImage(lastPImage[0].getId());
        }
        body.appendPageBreak();
      }
    }
    
    • When this script is used in your provided Document, I confirmed that your sample document could be merged into "Merged Document".

    Note:

    • This modified script is for your provided Document. When you change the Document, this script might not be able to be used. Please be careful about this.