I would like to ask for help with regard to a screenshot capture for a render window with svg inline elements. For example, let’s say for vtk.js, I would like to screenshot both the background + the text labels.
I have tried merging the data URIs for both the values obtained from renderWindow.captureImages() and encoding the svg element that is created with the text. However, that doesn’t seem to work (might be that I’m doing it wrongly). Is there a more native solution to this? Is there a way to include the svg text elements in the canvas/renderWindow’s captureImages func?
Hey, thanks! That really helped. I got it to work. Will post an example of my code soon that will work for any renderWindow with access to captureImages
// Note: This code is written in TypeScript + React, but you can use any other framework of choice.
// Note 2: Doing this way will unset styles from the inline svg element. You'll have to set them manually if you want to change the font-family for e.g.
// Let's pretend there's a <button onClick={takeScreenshot} >Screenshot</button>
function takeScreenshot(): void {
// My example's render window has an openGLRenderWindow as view
const renderWindow = view.renderWindow;
// Get reference to hidden anchor element for download by user (using useRef in React here)
const linkElement = screenshotLink.current;
// Guard check
if (!renderWindow || !linkElement) {
return;
}
// Get the svg inline element encased within the container of your renderWindow.
// It might be better to do widgetManager.getReferenceByName("svgRoot");
const svgElement = viewDiv.current?.getElementsByTagName('svg')[0];
// Define the images. We are going to write the render window image first to a proxy/fake canvas, before writing the svg to a combined img.
const renderImg = new Image();
const svgImg = new Image();
const proxyCanvas = document.createElement('canvas');
// Add event on load for svgImg to combine the images
svgImg.addEventListener('load', () => {
const ctx = proxyCanvas.getContext('2d');
if (!ctx) {
return;
}
// Draw the svg on top of our render image
ctx.drawImage(svgImg, 0, 0);
const imageType = `image/png`;
// Add the data url to a href and simulate click for user to save the image
linkElement.href = proxyCanvas.toDataURL(imageType);
linkElement.download = 'screenshot.png';
linkElement.click();
});
// Add event on load for initial render image
renderImg.addEventListener('load', () => {
const ctx = proxyCanvas.getContext('2d');
if (!ctx) {
return;
}
ctx.clearRect(0, 0, proxyCanvas.width, proxyCanvas.height);
proxyCanvas.width = renderImg.width;
proxyCanvas.height = renderImg.height;
ctx.drawImage(renderImg, 0, 0);
// This below checks if there is a svg element to be written as an image. We let it be an empty string if there isn't one, so it'll be a normal screenshot.
let svgSrc = '';
if (svgElement) {
svgElement.setAttribute('width', renderImg.width.toString());
svgElement.setAttribute('height', renderImg.height.toString());
const svgSerialized = new XMLSerializer().serializeToString(svgElement);
svgSrc = `data:image/svg+xml;base64,${Buffer.from(svgSerialized).toString('base64')}`;
}
// This triggers the svg image load function
svgImg.src = svgSrc;
});
// We apply renderWindow.captureImages() here and trigger the render image load function by appending the value to the render image source
const images = renderWindow.captureImages();
images[0].then((value: string) => {
renderImg.src = value;
});
}