From 8aad67ccfe58229b029d4a98cf68561f84127c62 Mon Sep 17 00:00:00 2001 From: Adarsh Singh <63918341+adarsh-sgh@users.noreply.github.com> Date: Sun, 5 Feb 2023 17:09:26 +0530 Subject: [PATCH] Desktop: Fixes #7521: Mermaid images are incorrectly sized when exported as PNG (#7546) --- .../gui/NoteEditor/utils/contextMenu.ts | 6 ++- .../gui/NoteEditor/utils/contextMenuUtils.ts | 38 ++++++++++++++++--- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts b/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts index ec08e18cf..2a2f904ad 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts @@ -2,7 +2,7 @@ import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher/index' import { _ } from '@joplin/lib/locale'; import { copyHtmlToClipboard } from './clipboardUtils'; import bridge from '../../../services/bridge'; -import { ContextMenuItemType, ContextMenuOptions, ContextMenuItems, resourceInfo, textToDataUri, svgUriToPng } from './contextMenuUtils'; +import { ContextMenuItemType, ContextMenuOptions, ContextMenuItems, resourceInfo, textToDataUri, svgUriToPng, svgDimensions } from './contextMenuUtils'; const Menu = bridge().Menu; const MenuItem = bridge().MenuItem; import Resource from '@joplin/lib/models/Resource'; @@ -106,8 +106,10 @@ export function menuItems(dispatch: Function): ContextMenuItems { if (!options.filename) { throw new Error('Filename is needed to save as png'); } + // double dimensions to make sure it's always big enough even on hdpi screens + const [width, height] = svgDimensions(document, options.textToCopy).map((x: number) => x * 2 || undefined); const dataUri = textToDataUri(options.textToCopy, options.mime); - const png = await svgUriToPng(document, dataUri); + const png = await svgUriToPng(document, dataUri, width, height); const filename = options.filename.replace('.svg', '.png'); await saveFileData(png, filename); }, diff --git a/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts b/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts index 723770002..2202bd558 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.ts @@ -1,5 +1,6 @@ import Resource from '@joplin/lib/models/Resource'; - +import Logger from '@joplin/lib/Logger'; +const logger = Logger.create('contextMenuUtils'); export enum ContextMenuItemType { None = '', Image = 'image', @@ -40,8 +41,23 @@ export async function resourceInfo(options: ContextMenuOptions) { export function textToDataUri(text: string, mime: string): string { return `data:${mime};base64,${Buffer.from(text).toString('base64')}`; } - -export const svgUriToPng = (document: Document, svg: string) => { +export const svgDimensions = (document: Document, svg: string) => { + let width: number; + let height: number; + try { + const parser = new DOMParser(); + const id = parser.parseFromString(svg, 'text/html').querySelector('svg').id; + ({ width, height } = document.querySelector('.noteTextViewer').contentWindow.document.querySelector(`#${id}`).getBoundingClientRect()); + } catch (error) { + logger.warn('Could not get SVG dimensions.'); + logger.warn('Error was: ', error); + } + if (!width || !height) { + return [undefined, undefined]; + } + return [width, height]; +}; +export const svgUriToPng = (document: Document, svg: string, width: number, height: number) => { return new Promise((resolve, reject) => { let canvas: HTMLCanvasElement; let img: HTMLImageElement; @@ -63,11 +79,21 @@ export const svgUriToPng = (document: Document, svg: string) => { try { canvas = document.createElement('canvas'); if (!canvas) throw new Error('Failed to create canvas element'); - canvas.width = img.width; - canvas.height = img.height; + if (!width || !height) { + const maxDimension = 1024; + if (img.width > img.height) { + width = maxDimension; + height = width * (img.height / img.width); + } else { + height = maxDimension; + width = height * (img.width / img.height); + } + } + canvas.width = width; + canvas.height = height; const ctx = canvas.getContext('2d'); if (!ctx) throw new Error('Failed to get context'); - ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height); + ctx.drawImage(img, 0, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height); const pngUri = canvas.toDataURL('image/png'); if (!pngUri) throw new Error('Failed to generate png uri'); const pngBase64 = pngUri.split(',')[1];