Search code examples
google-apps-scriptgoogle-docs

Update/Replace inline image on Google Document


I'm trying to set a feature to update images on a Google Document, the same way Lucidchart Add-on does on its "Updated inserted diagram" feature. For this, I'm current doing the following:

  • Creating a Named Range and storing its id on document properties, together with the data to generate the image, for later retrieve.
  • On update, call body.getNamedRangeById() and replace the element with the new generated image.

This works, but I have the following problems that does not happen with Lucidchart:

  • Every update, a blank line is added after the image.
  • If the user drag and drop the image inside document for reposition it, the Named Range disappears and I'm not able to retrieve it later.
  • If the user centralize the image, after update the image comes back to left position, even copying its attributes

Does anybody knows a good strategy to replace/update a referenced image on Google Docs, the same way Lucidchart add-on update feature works?

Thanks


Solution

  • NamedRanges indeed get lost when the range is moved, so they're not very good for your scenario. But there's no other way of identifying elements (which is a great misfeature of Google Docs).

    In the case of an image you could use its LINK_URL to identify it, which seems to be what Lucidchart uses. It does not get in the way of the user, so it may be a good solution.

    About getting a blank line and losing attributes when inserting an image, I imagine (since you haven't shared any code) you're inserting the image directly in the document body instead of a paragraph. Then a paragraph gets created automatically to wrap your image resulting in the blank line and lost of attributes.

    Here's some code example:

    function initialInsert() {
      var data = Charts.newDataTable().addColumn(
        Charts.ColumnType.STRING, 'Fruits').addColumn(
        Charts.ColumnType.NUMBER, 'Amount').addRow(
        ['Apple',15]).addRow(
        ['Orange',6]).addRow(
        ['Banana',14]).build();
      var chart = Charts.newPieChart().setDataTable(data).build();
    
      var body = DocumentApp.getActiveDocument().getBody()
      body.appendImage(chart).setLinkUrl('http://mychart');
      //here we're inserting directly in the body, a wrapping paragraph element will be created for us
    }
    
    function updateImage() {
      var data = Charts.newDataTable().addColumn(
        Charts.ColumnType.STRING, 'Fruits').addColumn(
        Charts.ColumnType.NUMBER, 'Amount').addRow(
        ['Apple',Math.floor(Math.random()*31)]).addRow( //random int between 0 and 30
        ['Orange',Math.floor(Math.random()*31)]).addRow(
        ['Banana',Math.floor(Math.random()*31)]).build();
      var chart = Charts.newPieChart().setDataTable(data).build();
    
      var img = getMyImg(DocumentApp.getActiveDocument().getBody(), 'http://mychart');
      //let's insert on the current parent instead of the body 
      var parent = img.getParent(); //probably a paragraph, but does not really matter
      parent.insertInlineImage(parent.getChildIndex(img)+1, chart).setLinkUrl('http://mychart');
      img.removeFromParent();
    }
    
    function getMyImg(docBody, linkUrl) {
      var imgs = docBody.getImages();
      for( var i = 0; i < imgs.length; ++i )
        if( imgs[i].getLinkUrl() === linkUrl )
          return imgs[i];
      return null;
    }
    

    About the link_url, you could of course do like Lucidchart does and link back to your site. So it's not just broken for the user.