Master Document in Google Drive using Google Apps Script

Word and LibreOffice can do this as a built-in feature but Sharepoint and Google Drive/Docs can’t. Given that Sharepoint is a nightmare to just use (!) I tackled the problem on Google Drive. What we are seeking to achieve is to have a main or master document that pulls in other documents, correctly formatted to make a full document. This is useful when “writing a book”, or creating a Health and Safety file or Employee Handbook. Once again I can’t take all the credit for a lot of the coding, the initial script to pull in formatted data from other documents is due to Henrique Abreu with additional help from Mogsdad and Serge Insas.

On reflection I could have stuck with the array approach as in the original script, but I broke things down to individual functions in order to keep track of what was going on and to be able to easily identify, add and modify the sub document contents.

Here is how to do it:

Workflow:

    1. In Google Drive create sub documents, then create a master document.
    2. Create a title page document, to make the first page to import to the master document (explained later!)
    3. All the documents can have headers and footers, these are not carried over (by choice) when the master document is compiled.
    4. Once all the sub documents and title page are created, go to the master document, click Tools and open Script Editor, choose blank script.

OK, a bit of explanation required:

  1. You will need to document IDs for each of the sub documents (I have only used two documents in this sample script). This should be the only change you need to make.
  2. The merging script won’t run on an “onOpen” trigger, so you have to create a custom menu in order to create the master document. The menu includes options for clearing the master document, creating it (only do this after a clear), a PDF creation, and a combination of all three. This last one is the most likely to be used.
  3. Simply add more functions for each additional document you require to be merged, in the order you want them. (e.g function doc3, function doc4).
  4. Always use the function doc1 for the title page, this function does not include a page break. We need the title/first page because the clearit function removes all body content.
  5. I have uncommented

    as this allows bullet points to copy across with the correct format.
  6. One could set a time driven trigger to keep the document created and up to date.
  7. The PDF creation helps to create a master document at a point in time, especially important if there are many collaborators at work!

Hope this all makes sense, the links to the original contributors will help :)

12 thoughts on “Master Document in Google Drive using Google Apps Script

  1. Hey Love this I have been looking to do something like this for a while

    I changed one function because I had more docs
    function allDocs() {
    var docList = [“docId1”,
    “docId2”,
    “docId3”,
    “docId4”,
    “docId5”
    ]

    for(var i = 0; i < docList.length; i++){
    if(i == 0){
    mergeDocs(docList[i]);
    }
    else{
    var doc = DocumentApp.getActiveDocument();
    var body = doc.getBody();
    body.appendPageBreak();

    mergeDocs(docList[i]);
    }
    }
    }
    I'm having some trouble with the pdf function I'm getting the error
    ReferenceError: "DocsList" is not defined.
    Is that a library or something? I'm not familiar with the google app scripts api.

  2. So I found that DocsList has been depricated they recommend using DriveApp so my fonvertPDF function looks like this
    function convertPDF() {
    var myDate = Utilities.formatDate(new Date(), “GMT”, “yyyy-MM-dd’-‘HHmm”);
    doc = DocumentApp.getActiveDocument();
    docblob = DocumentApp.getActiveDocument().getAs(‘application/pdf’);
    try {
    folder = DriveApp.getFoldersByName(‘Converted to PDF’).next();
    }
    catch (e) {
    folder = DriveApp.createFolder(‘Converted to PDF’);
    }
    docblob.setName(doc.getName()+’-‘+myDate)
    folder.createFile(docblob);
    }

  3. Thank you for posting the scripts to merge documents (master and sub
    documents). It works for small files but not in production. (I am preparing
    the textbook for the course I teach on Computer Organization II.)

    When, I try to merge a larger file, it stops after 25 or 175 paragraphs, respectively.
    I get the error message “Service unavailable: Docs ” I put a log
    message in your main loop to count the number of paragraphs. It always ends on the same paragraph.

    I prepared the following to test. When, I uncomment the line body.appendParagraph(element) in countparas, I get the Service unavailable error.

    However, the debugging displaying the counts and paragraph contents are the same
    whether it is actually adding to my master document or not. Nothing is in the
    master document, apparently, the service unavailable error clears the master document
    being created.

    P. S. my subdocuments have footnotes. I did not see anything in your code to handle
    them. Do I have to put in code to search the paragraphs for footnotes, as you do
    for images.

    function test2() {
    var doc = DocumentApp.getActiveDocument();
    var body = doc.getBody();
    body.appendPageBreak();
    Logger.log(“COUTNT DOC TWO”);

    var id;
    id = docFind(“400加两个数字”);
    countParas(id,false);
    Logger.log(‘COUTNT DOC THREE |’+id+’|’);

    countParas(id,false);
    id = docFind(“400H子程序”);
    Logger.log(‘third document |’+id+’|’);
    countParas(id,false);
    }

    function countParas(docID,LF){
    var docIDs = docID;
    var sourceDoc = DocumentApp.openById(docIDs);
    var sourceBody = sourceDoc.getBody();
    var totalElements = sourceBody.getNumChildren();
    var latestElement;
    var count;
    count = 0;
    var baseDoc = DocumentApp.getActiveDocument();
    var body = baseDoc.getBody();
    for( var j = 0; j < totalElements; ++j ) {
    var element = sourceBody.getChild(j).copy();
    type = element.getType();
    if (type ==DocumentApp.ElementType.PARAGRAPH) {
    count ++;
    Logger.log("merging element "+j+"|"+element+"|");
    var para = element.asParagraph();
    Logger.log(para.getText());
    body.appendParagraph(element);// this s the line causing the service unavailable
    }
    }
    Logger.log("finished processing "+j+ " count " + count);
    }
    _______________________________________________________
    I found it useful, just to use the file name in the script in the master document
    making use of the following function.

    function docFind(filename){
    Logger.log("finding |"+filename+"|");
    var files =DriveApp.getFiles();
    while(files.hasNext()){
    f = files.next();
    if (filename==f.getName()){
    return f.getId();
    }
    }
    Logger.log("did not find |"+filename+"|");
    return null;
    }
    ______________________________

    • Hi Lawrence

      Thanks for you input – getting complicated

      Regarding footnotes, if they are in the footer of the pages, there is an indexing system that can get you to them, this is unlikely for you though as then they would show up on every page! I have a script somewhere that grabs the date that is in the footer of every document.

      If your footnotes are in a table you could find the table and get the content, otherwise it may just be a matter of traversing a document and to see if there are any special characteristics of your footer in Google docs terms that will allow you to grab the content.

  4. Thank you for your erudite discussion of fottnotes. However, do you have any suggestions regarding troubleshooting the error message “Service unavailable: Docs ” that is coming from an attempt to move the paragraph from one document to another.

    I fear that I will just simply have to cut and paste each of the documents into a single document to prepare the class notes.

    • Do you get the same problem if you use my original script? If so it might point to the document or format of the documents, if not, something in your new code, I am unable to see/debug.

  5. Thank you again for following up so extensively
    on my use of your code to handle sub documents
    within Google Docs. As per our discussion, I thought the problem was the
    foot notes. Thus, I did my test with my code but after deleting the footnotes
    from the test subdocuments. I then got about thirty pages of blank pages
    from one of the two test documents. I tried again and it did not load.
    However, the error message that the service unavailable was no longer
    present. Thus, I conclude that footnotes in a subdocument are causing
    your code to fail this way.

    At this point, I had already manually just included the files I needed
    into my textbook. Quite frankly, Google Apps is so wonderfully snappy on
    this 135-page document that I see no reason to keep the textbook as a
    collection of subdocuments, even if software to handle them worked
    perfectly magically ended up in my box. (I don’t have collaborators.)

    When I next have a textbook to write for a course assigned to me to teach,
    I will however look again at these issues. Furthermore, your hard work
    and willingness to share mean that when I am comfortable exploring AppScript
    when such ocassion arrives.

  6. As per our discussion, I thought the problem was the
    foot notes. Thus, I did my test with my code but after deleting the footnotes
    from the test subdocuments. I then got about thirty pages of blank pages
    from one of the two test documents. I tried again and it did not load.
    However, the error message that the service unavailable was no longer
    present. Thus, I conclude that footnotes in a subdocument are causing
    your code to fail this way.

    At this point, I had already manually just included the files I needed
    into my textbook. Quite frankly, Google Apps is so wonderfully snappy on
    this 135-page document that I see no reason to keep the textbook as a
    collection of subdocuments, even if software to handle them worked
    perfectly magically ended up in my box. (I don’t have collaborators.)

    When I next have a textbook to write for a course assigned to me to teach,
    I will however look again at these issues. Furthermore, your hard work
    and willingness to share mean that when I am comfortable exploring AppScript
    when such ocassion arrives.

Leave a Reply

Your email address will not be published. Required fields are marked *