问题
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
回答1:
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.
回答2:
Take a look at my add-on called PlantUML Gizmo.
Here's the code to the insert image function, which deals with replacing images if there's already one selected:
function insertImage(imageDataUrl, imageUrl) {
/*
* For debugging cursor info
*/
// var cursor = DocumentApp.getActiveDocument().getCursor();
// Logger.log(cursor.getElement().getParent().getType());
// throw "cursor info: " + cursor.getElement().getType() + " offset = " + cursor.getOffset() + " surrounding text = '" + cursor.getSurroundingText().getText() + "' parent's type = " +
// cursor.getElement().getParent().getType();
/*
* end debug
*/
var doc = DocumentApp.getActiveDocument();
var selection = doc.getSelection();
var replaced = false;
if (selection) {
var elements = selection.getSelectedElements();
// delete the selected image (to be replaced)
if (elements.length == 1 &&
elements[0].getElement().getType() ==
DocumentApp.ElementType.INLINE_IMAGE) {
var parentElement = elements[0].getElement().getParent(); // so we can re-insert cursor
elements[0].getElement().removeFromParent();
replaced = true;
// move cursor to just before deleted image
doc.setCursor(DocumentApp.getActiveDocument().newPosition(parentElement, 0));
} else {
throw "Please select only one image (image replacement) or nothing (image insertion)"
}
}
var cursor = doc.getCursor();
var blob;
if (imageDataUrl != "") {
blob = getBlobFromBase64(imageDataUrl);
} else {
blob = getBlobViaFetch(imageUrl);
}
var image = cursor.insertInlineImage(blob);
image.setLinkUrl(imageUrl);
// move the cursor to after the image
var position = doc.newPosition(cursor.getElement(), cursor.getOffset()+1);
doc.setCursor(position);
if (cursor.getElement().getType() == DocumentApp.ElementType.PARAGRAPH) {
Logger.log("Resizing");
// resize if wider than current page
var currentParagraph = DocumentApp.getActiveDocument().getCursor().getElement().asParagraph();
var originalImageWidth = image.getWidth(); // pixels
var documentWidthPoints = DocumentApp.getActiveDocument().getBody().getPageWidth() - DocumentApp.getActiveDocument().getBody().getMarginLeft() - DocumentApp.getActiveDocument().getBody().getMarginRight();
var documentWidth = documentWidthPoints * 96 / 72; // convert to pixels (a guess)
var paragraphWidthPoints = documentWidthPoints - currentParagraph.getIndentStart() - currentParagraph.getIndentEnd();
var paragraphWidth = paragraphWidthPoints * 96 / 72; // convert to pixels (a guess)
if (originalImageWidth > paragraphWidth) {
image.setWidth(paragraphWidth);
// scale proportionally
image.setHeight(image.getHeight() * image.getWidth() / originalImageWidth);
}
}
}
来源:https://stackoverflow.com/questions/26597379/update-replace-inline-image-on-google-document