A repo and NPM package for Office.js, corresponding to a copy of what gets published to the official "evergreen" Office.js CDN, at https://appsforoffice.microsoft.com/lib/1/hosted/office.js.
I'm trying to replace some images in a slide with their name & a new image in base64. To do it I get all the shapes, filter the ones with the name corresponding to an id given as parameter, create an image at the same position, remove old shape and set to the one created the id as name to find it again if wanted.
But after the new image is created, all context.sync() can run indefinitely, and all function as XXX.load('') may not work and throw an error as the desired element are not loaded. It's hard to explain more as this bug seems to be random and does not appear all the time.
Environment:
Platform: Office on the Web
Host: Powerpoint
Operating System: Windows 11
Browser: FireFox (V131.0.3)
Expected behavior
All the context.sync() after the insertion of images should run as the context.sync() before the insertion of images. So just by loading correctly the desired elements and do not last an infinite amount of time.
Current behavior
Most of the time the function never end because some context.sync() never end. It always happen after the creation of the images, when I reload all shapes to set the desired name to the new added shapes. The problems come from this point.
My functions with in comment the moment of problems
/*
* Function to add the image in the slide
* https://learn.microsoft.com/en-us/office/dev/add-ins/develop/read-and-write-data-to-the-active-selection-in-a-document-or-spreadsheet
* @param {string} image The string image code to create
* @param {{left:number,top:number}} position The position of the image
* @returns {Promise<boolean>}
*/
async function importImage(
image: string,
position: {
left: number,
top: number
},
) {
return new Promise((resolve, reject) => {
Office.context.document.setSelectedDataAsync(
image,
{
coercionType: Office.CoercionType.Image,
imageLeft: position.left,
imageTop: position.top,
},
(result) => {
if (result.status === Office.AsyncResultStatus.Failed) {
return reject(result.error.message)
}
return resolve(true)
})
})
}
/**
* Function to replace the image with id given id
* @param {string} uuid The id of the image to replace. If no shape with this name, the image will not be created
* @param {string} image The code of the image
* @returns {Promise<boolean>}
*/
async function replaceImages(datas: {
uuid: string,
image: string
}[]): Promise<boolean> {
if (!Office.context.document) throw new Error('Can\'t get context of Office Document')
return PowerPoint.run(async (context) => {
// Get the current slide
let slides = context.presentation.getSelectedSlides()
let currentSlide = slides.getItemAt(0)
currentSlide.load('shapes, shapes/name')
await context.sync()
// Get the shapes to update
const shapes = currentSlide.shapes.items.filter(shape => {
return datas.findIndex(data => data.uuid === shape.name) > -1
})
if(!shapes?.length) return Promise.resolve(false)
// Load position of the shapes to replace
shapes.forEach(shape => {
shape.load('left, top')
})
await context.sync()
// Create the new images and remove the old one
const uuidsReplaced: string[] = []
for(const shape of shapes) {
const data = datas.find(data => data.uuid === shape.name)!
uuidsReplaced.push(data.uuid)
await importImage(data.image, {left: shape.left, top: shape.top })
shape.delete()
}
// The new shape is Added and old one deleted, but the name in the new image is not set yet to find it again if wanted
// ! Problem from here. At this point, each context.sync() can last forever of badly load requirement and throw an error
await context.sync()
// get again all shapes
slides = context.presentation.getSelectedSlides()
currentSlide = slides.getItemAt(0)
currentSlide.load('shapes, shapes/name')
await context.sync()
// The Added images are the {shapes.length} last shapes in collection
const shapeCollection = [...currentSlide.shapes.items]
const newShapes = shapeCollection.slice(shapeCollection.length - shapes.length)
// Set the name to get it again if I want
newShapes.forEach((newShape, index) => {
newShape.name = uuidsReplaced[index]
})
await context.sync()
return Promise.resolve(true)
}).catch((error) => {
console.error(error)
return Promise.resolve(false)
})
}
I had better result by adding a timer of 1-2 seconds between the shape.delete() and the next await context.sync(). But sometimes it still struggling and never finish.
I'm trying to replace some images in a slide with their name & a new image in base64. To do it I get all the shapes, filter the ones with the name corresponding to an id given as parameter, create an image at the same position, remove old shape and set to the one created the id as name to find it again if wanted.
But after the new image is created, all
context.sync()
can run indefinitely, and all function as XXX.load('') may not work and throw an error as the desired element are not loaded. It's hard to explain more as this bug seems to be random and does not appear all the time.Environment:
Expected behavior
All the
context.sync()
after the insertion of images should run as thecontext.sync()
before the insertion of images. So just by loading correctly the desired elements and do not last an infinite amount of time.Current behavior
Most of the time the function never end because some
context.sync()
never end. It always happen after the creation of the images, when I reload all shapes to set the desired name to the new added shapes. The problems come from this point.My functions with in comment the moment of problems
I had better result by adding a timer of 1-2 seconds between the
shape.delete()
and the nextawait context.sync()
. But sometimes it still struggling and never finish.