From 7ae672e01c321f8966a4ef4733fe70cf5d4ccf35 Mon Sep 17 00:00:00 2001 From: Leandro Treu Date: Thu, 28 Mar 2024 17:33:58 +0100 Subject: [PATCH] PDF export fixes (#139) * bind viewport to graph * bind viewport to graph bounds && comments * first LOD optimizations * minimap optimizations * quickfix simple_draw after invisible check * added connector LOD * first mousehandler optimizations (only highlight when close) * mousehandler highlighting optimization (only redraw on change) * arrowhead LOD * fixed connectors rendered when invisible * fixed edges rendered even if not visible * added loading animation for relayout tasks * added missing loading animation for settings change * fixed mousehandler highlighting and solved state-outline performance issues * changed state LOD formula * removed adaptiveHideContent option from user settings menu * reworked user settings menu, added curved edges setting, added adaptiveStateHiding setting again with a warning * checked all lod occurrences * added loading animation to settings menu * fixed pdf export issues * added loading animation for pdf export --- src/renderer/renderer.ts | 120 ++++++++++++++++++------------ src/renderer/renderer_elements.ts | 26 ++++--- 2 files changed, 89 insertions(+), 57 deletions(-) diff --git a/src/renderer/renderer.ts b/src/renderer/renderer.ts index d3ff04fa..d88422e7 100644 --- a/src/renderer/renderer.ts +++ b/src/renderer/renderer.ts @@ -1454,57 +1454,81 @@ export class SDFGRenderer extends EventEmitter { } public save_as_pdf(save_all = false): void { - const stream = blobStream(); + + this.add_loading_animation(); - // Compute document size - const curx = this.canvas_manager?.mapPixelToCoordsX(0); - const cury = this.canvas_manager?.mapPixelToCoordsY(0); - let size; - if (save_all) { - // Get size of entire graph - const elements: SDFGElement[] = []; - this.graph?.nodes().forEach((n_id: string) => { - const node = this.graph?.node(n_id); - if (node) - elements.push(node); + // Use setTimeout to force browser to update the DOM with the above loading animation + setTimeout(() => { + + const stream = blobStream(); + + // Compute document size + const curx = this.canvas_manager?.mapPixelToCoordsX(0); + const cury = this.canvas_manager?.mapPixelToCoordsY(0); + let size; + if (save_all) { + // Get size of entire graph + const elements: SDFGElement[] = []; + this.graph?.nodes().forEach((n_id: string) => { + const node = this.graph?.node(n_id); + if (node) + elements.push(node); + }); + const bb = boundingBox(elements); + size = [bb.width, bb.height]; + } else { + // Get size of current view + const canvasw = this.canvas?.width; + const canvash = this.canvas?.height; + let endx = null; + if (canvasw) + endx = this.canvas_manager?.mapPixelToCoordsX(canvasw); + let endy = null; + if (canvash) + endy = this.canvas_manager?.mapPixelToCoordsY(canvash); + const curw = (endx ? endx : 0) - (curx ? curx : 0); + const curh = (endy ? endy : 0) - (cury ? cury : 0); + size = [curw, curh]; + } + + const ctx = new canvas2pdf.PdfContext(stream, { + size: size }); - const bb = boundingBox(elements); - size = [bb.width, bb.height]; - } else { - // Get size of current view - const canvasw = this.canvas?.width; - const canvash = this.canvas?.height; - let endx = null; - if (canvasw) - endx = this.canvas_manager?.mapPixelToCoordsX(canvasw); - let endy = null; - if (canvash) - endy = this.canvas_manager?.mapPixelToCoordsY(canvash); - const curw = (endx ? endx : 0) - (curx ? curx : 0); - const curh = (endy ? endy : 0) - (cury ? cury : 0); - size = [curw, curh]; - } - // - - const ctx = new canvas2pdf.PdfContext(stream, { - size: size - }); - const oldctx = this.ctx; - this.ctx = ctx; - (this.ctx as any).lod = !save_all; - (this.ctx as any).pdf = true; - // Center on saved region - if (!save_all) - this.ctx?.translate(-(curx ? curx : 0), -(cury ? cury : 0)); - - this.draw_async(); - - ctx.stream.on('finish', () => { - const name = this.sdfg.attributes.name; - this.save(name + '.pdf', ctx.stream.toBlobURL('application/pdf')); - this.ctx = oldctx; + const oldctx = this.ctx; + this.ctx = ctx; + + // Necessary for "what you see is what you get" in the exported pdf file + if (!save_all && (oldctx as any).lod) { + // User wants to save the view "as is" with details hidden + (this.ctx as any).lod = true; + } + else { + // User wants to save all details in the view + (this.ctx as any).lod = false; + } + (this.ctx as any).pdf = true; + // Center on saved region + if (!save_all) + this.ctx?.translate(-(curx ? curx : 0), -(cury ? cury : 0)); + this.draw_async(); - }); + + ctx.stream.on('finish', () => { + const name = this.sdfg.attributes.name; + this.save(name + '.pdf', ctx.stream.toBlobURL('application/pdf')); + this.ctx = oldctx; + this.draw_async(); + // Remove loading animation + const info_field = document.getElementById('task-info-field'); + if (info_field) { + info_field.innerHTML = ""; + } + const info_field_settings = document.getElementById('task-info-field-settings'); + if (info_field_settings) { + info_field_settings.innerHTML = ""; + } + }); + }, 10); } // Draw a debug grid on the canvas to indicate coordinates. diff --git a/src/renderer/renderer_elements.ts b/src/renderer/renderer_elements.ts index 09907a60..85a4ba81 100644 --- a/src/renderer/renderer_elements.ts +++ b/src/renderer/renderer_elements.ts @@ -1375,14 +1375,21 @@ export class InterstateEdge extends Edge { const labelWs = []; for (const l of labelLines) { const labelMetrics = ctx.measureText(l); - labelWs.push( - Math.abs(labelMetrics.actualBoundingBoxLeft) + - Math.abs(labelMetrics.actualBoundingBoxRight) - ); - labelHs.push( - Math.abs(labelMetrics.actualBoundingBoxDescent) + - Math.abs(labelMetrics.actualBoundingBoxAscent) - ); + + let label_width = Math.abs(labelMetrics.actualBoundingBoxLeft) + + Math.abs(labelMetrics.actualBoundingBoxRight); + let label_height = Math.abs(labelMetrics.actualBoundingBoxDescent) + + Math.abs(labelMetrics.actualBoundingBoxAscent); + + // In case of canvas2pdf context, that only has width and height + // as TextMetrics properties + if (label_width != label_width) + label_width = (labelMetrics as any).width; + if (label_height != label_height) + label_height = (labelMetrics as any).height; + + labelWs.push(label_width); + labelHs.push(label_height); } const labelW = Math.max(...labelWs); const labelH = labelHs.reduce((pv, cv) => { @@ -1448,12 +1455,13 @@ export class InterstateEdge extends Edge { ctx.fillStyle = this.getCssProperty( renderer, '--interstate-edge-color' ); - for (let i = 0; i < labelLines.length; i++) + for (let i = 0; i < labelLines.length; i++) { ctx.fillText( labelLines[i], srcP.x + offsetX, (srcP.y + offsetY) - (i * (labelHs[0] + SDFV.LINEHEIGHT)) ); + } ctx.font = oldFont; }