We currently have a PDF loaded into Forge viewer with multiple markup layers (created using a custom DrawMode), each layers visibility toggleable.
We want to give the user the ability of printing what they currently see (the PDF with the layered markup). I was able to find posts offering potential solutions for printing (using canvas, getScreenshot and MarkupUtils renderToCanvas). Example post: Autodesk Forge get screenshot with markups
The solution at first looked to be working great but I've noticed only one of our markup layers is ever rendered to the canvas (seemingly the last one added), the other layers are ignored.
All markups are loaded and are visible on screen. Additionally if I hide that layer, it is still printed.
Is there any way to add the markup from all loaded markup layers using renderToCanvas? Or any potential known workaround?
Any help appreciated. Thanks in advance.
Code snippet of the function I've wrote which works (but loading the most recently added layer only).
export const printViewerToPDF = (markupsCore: MarkupsCore, jsPDF: any) => {
// Create new image
var screenshot = new Image();
// Get the canvas element
var canvas = document.getElementById('snapshot') as HTMLCanvasElement;
// Fit canvas to match viewer
if (canvas) {
canvas.width = markupsCore.bounds.width;
canvas.height = markupsCore.bounds.height;
// Create a context
var ctx = canvas.getContext('2d');
// Clear
if (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(screenshot, 0, 0, canvas.width, canvas.height);
}
// Screenshot viewer and render to canvas
markupsCore.viewer.getScreenShot(canvas.width, canvas.height, function(
blobUrl: any,
) {
screenshot.onload = function() {
if (ctx) {
ctx.drawImage(screenshot, 0, 0);
}
};
screenshot.src = blobUrl;
});
// Render markup to canvas
setTimeout(function() {
markupsCore.renderToCanvas(ctx, function() {
var pdf = new jsPDF('l', 'px', [canvas.height, canvas.width]);
pdf.addImage(ctx!.canvas, 0, 0, canvas.width, canvas.height);
pdf.save(Date.now().toString() + '.pdf');
});
}, 300);
}
};
Here's what the renderToCanvas
method looks like (you can find it in https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/extensions/Markup/Markup.js):
MarkupsCore.prototype.renderToCanvas = function(context, callback, renderAllMarkups) {
var width = this.bounds.width;
var height = this.bounds.height;
var viewBox = this.getSvgViewBox(width, height);
var numberOfScreenshotsTaken = 0;
var markups = [];
var layer;
var onMarkupScreenshotTaken = function () {
if (callback && (++numberOfScreenshotsTaken === markups.length)) {
callback();
}
}.bind(this);
if (renderAllMarkups) {
var svgKeys = Object.keys(this.svg.childNodes);
var layersKeys = Object.keys(this.svgLayersMap);
// Append only markups that their parent layer is contained inside the svg main container.
for (var i = 0; i < svgKeys.length; i++) {
for (var j = 0; j < layersKeys.length; j++) {
layer = this.svgLayersMap[layersKeys[j]];
if (this.svg.childNodes[svgKeys[i]] === layer.svg) {
markups = markups.concat(layer.markups);
}
}
}
} else {
layer = this.svgLayersMap[this.activeLayer] || this.editModeSvgLayerNode;
markups = layer.markups;
}
if (markups.length === 0) {
callback();
} else {
markups.forEach(function(markup) {
markup.renderToCanvas(context, viewBox, width, height, onMarkupScreenshotTaken);
});
}
};
As you can see, if you leave the 3rd parameter undefined or set to false
, only markups from the active layer will be rendered. If you set the 3rd parameter to true
, markups from all layers should be rendered.
Try stepping into the method yourself and double-check the list of markups towards the end, before markup.renderToCanvas
is called for every item in the list.