diff --git a/src/core/main.ts b/src/core/main.ts index 22f5dec..228ccab 100644 --- a/src/core/main.ts +++ b/src/core/main.ts @@ -91,6 +91,15 @@ import { EventManager } from "./managers/EventManager"; declare const PLUGIN_VERSION:string; declare const INITIAL_TIMESTAMP: number; +type FileMasterInfo = { + isHyperLink: boolean; + isLocalLink: boolean; + path: string; + hasSVGwithBitmap: boolean; + blockrefData: string, + colorMapJSON?: string +} + export default class ExcalidrawPlugin extends Plugin { private fileManager: PluginFileManager; private observerManager: ObserverManager; @@ -113,7 +122,7 @@ export default class ExcalidrawPlugin extends Plugin { public opencount: number = 0; public ea: ExcalidrawAutomate; //A master list of fileIds to facilitate copy / paste - public filesMaster: Map = + public filesMaster: Map = null; //fileId, path public equationsMaster: Map = null; //fileId, formula public mermaidsMaster: Map = null; //fileId, mermaidText diff --git a/src/shared/CropImage.ts b/src/shared/CropImage.ts index b119d15..eb00371 100644 --- a/src/shared/CropImage.ts +++ b/src/shared/CropImage.ts @@ -9,6 +9,30 @@ import { ExportSettings } from "src/view/ExcalidrawView"; import { nanoid } from "src/constants/constants"; import { svgToBase64 } from "../utils/utils"; +/** + * Creates a masked image from an Excalidraw scene. + * + * The scene must contain: + * - One element.type="frame" element that defines the crop area + * - One or more element.type="image" elements + * - Zero or more non-image shape elements (rectangles, ellipses etc) that define the mask + * + * The class splits the scene into two parts: + * 1. Images (managed in imageEA) + * 2. Mask shapes (managed in maskEA) + * + * A transparent rectangle matching the combined bounding box is added to both + * imageEA and maskEA to ensure consistent sizing between image and mask. + * + * For performance, if there is only one image, it is not rotated, and + * its size matches the bounding box, + * the image data is used directly from cache rather than regenerating. + * + * @example + * const cropper = new CropImage(elements, files); + * const pngBlob = await cropper.getCroppedPNG(); + * cropper.destroy(); + */ export class CropImage { private imageEA: ExcalidrawAutomate; private maskEA: ExcalidrawAutomate; @@ -106,10 +130,15 @@ export class CropImage { withTheme: false, isMask: false, } - const isRotated = this.imageEA.getElements().some(el=>el.type === "image" && el.angle !== 0); - const images = Object.values(this.imageEA.imagesDict); - if(!isRotated && (images.length === 1)) { - return images[0].dataURL; + const images = this.imageEA.getElements().filter(el=>el.type === "image" && el.isDeleted === false); + const isRotated = images.some(el=>el.angle !== 0); + const imageDataURLs = Object.values(this.imageEA.imagesDict); + if(!isRotated && images.length === 1 && imageDataURLs.length === 1) { + const { width, height } = this.bbox; + if(images[0].width === width && images[0].height === height) { + //get image from the cache if mask is not bigger than the image, and if there is a single image element + return imageDataURLs[0].dataURL; + } } return await this.imageEA.createPNGBase64(null,1,exportSettings,null,null,0); } diff --git a/src/shared/Dialogs/SuggesterInfo.ts b/src/shared/Dialogs/SuggesterInfo.ts index a89e860..0a2e86f 100644 --- a/src/shared/Dialogs/SuggesterInfo.ts +++ b/src/shared/Dialogs/SuggesterInfo.ts @@ -157,6 +157,15 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [ desc: "Set ea.style.roundness. 0: is the legacy value, 3: is the current default value, null is sharp", after: "", }, + { + field: "addAppendUpdateCustomData", + code: "addAppendUpdateCustomData(id: string, newData: Partial>)", + desc: "Add, modify keys in element customData and preserve existing keys.\n" + + "Creates customData={} if it does not exist.\n" + + "Takes the element ID for an element in the elementsDict and the new data to add or modify.\n" + + "To delete keys set key value in newData to undefined. so {keyToBeDeleted:undefined} will be deleted.", + after: "", + }, { field: "addToGroup", code: "addToGroup(objectIds: []): string;", diff --git a/src/shared/EmbeddedFileLoader.ts b/src/shared/EmbeddedFileLoader.ts index 8ba4e88..c2fc540 100644 --- a/src/shared/EmbeddedFileLoader.ts +++ b/src/shared/EmbeddedFileLoader.ts @@ -73,7 +73,8 @@ type ImgData = { dataURL: DataURL; created: number; hasSVGwithBitmap: boolean; - size: { height: number; width: number }; + size: Size; + pdfPageViewProps?: PDFPageViewProps; }; export declare type MimeType = ValueOf | "application/octet-stream"; @@ -82,8 +83,16 @@ export type FileData = BinaryFileData & { size: Size; hasSVGwithBitmap: boolean; shouldScale: boolean; //true if image should maintain its area, false if image should display at 100% its size + pdfPageViewProps?: PDFPageViewProps; }; +export type PDFPageViewProps = { + left: number; + bottom: number; + right: number; + top: number; +} + export type Size = { height: number; width: number; @@ -177,6 +186,7 @@ export class EmbeddedFile { public isLocalLink: boolean = false; public hyperlink:DataURL; public colorMap: ColorMap | null = null; + public pdfPageViewProps: PDFPageViewProps; constructor(plugin: ExcalidrawPlugin, hostPath: string, imgPath: string, colorMapJSON?: string) { this.plugin = plugin; @@ -252,12 +262,14 @@ export class EmbeddedFile { return this.mtime !== this.file.stat.mtime; } - public setImage( - imgBase64: string, - mimeType: MimeType, - size: Size, - isDark: boolean, - isSVGwithBitmap: boolean, + public setImage({ imgBase64, mimeType, size, isDark, isSVGwithBitmap, pdfPageViewProps } : { + imgBase64: string; + mimeType: MimeType; + size: Size; + isDark: boolean; + isSVGwithBitmap: boolean; + pdfPageViewProps?: PDFPageViewProps; + } ) { if (!this.file && !this.isHyperLink && !this.isLocalLink) { return; @@ -266,6 +278,7 @@ export class EmbeddedFile { this.imgInverted = this.img = ""; } this.mtime = this.isHyperLink || this.isLocalLink ? 0 : this.file.stat.mtime; + this.pdfPageViewProps = pdfPageViewProps; this.size = size; this.mimeType = mimeType; switch (isDark && isSVGwithBitmap) { @@ -345,6 +358,7 @@ export class EmbeddedFilesLoader { created: number; hasSVGwithBitmap: boolean; size: { height: number; width: number }; + pdfPageViewProps?: PDFPageViewProps; }> { const result = await this._getObsidianImage(inFile, depth); this.emptyPDFDocsMap(); @@ -552,9 +566,9 @@ export class EmbeddedFilesLoader { const excalidrawSVG = isExcalidrawFile ? dURL : null; - const [pdfDataURL, pdfSize] = isPDF + const [pdfDataURL, pdfSize, pdfPageViewProps] = isPDF ? await this.pdfToDataURL(file,linkParts) - : [null, null]; + : [null, null, null]; let mimeType: MimeType = isPDF ? "image/png" @@ -600,6 +614,7 @@ export class EmbeddedFilesLoader { created: isHyperLink || isLocalLink ? 0 : file.stat.mtime, hasSVGwithBitmap, size, + pdfPageViewProps, }; } catch(e) { return null; @@ -634,7 +649,7 @@ export class EmbeddedFilesLoader { files.push([]); let batch = 0; - function* loadIterator():Generator> { + function* loadIterator(this: EmbeddedFilesLoader):Generator> { while (!(entry = entries.next()).done) { if(fileIDWhiteList && !fileIDWhiteList.has(entry.value[0])) continue; const embeddedFile: EmbeddedFile = entry.value[1]; @@ -654,20 +669,22 @@ export class EmbeddedFilesLoader { created: data.created, size: data.size, hasSVGwithBitmap: data.hasSVGwithBitmap, - shouldScale: embeddedFile.shouldScale() + shouldScale: embeddedFile.shouldScale(), + pdfPageViewProps: data.pdfPageViewProps, }; files[batch].push(fileData); } } else if (embeddedFile.isSVGwithBitmap && (depth !== 0 || isThemeChange)) { //this will reload the image in light/dark mode when switching themes - const fileData = { + const fileData: FileData = { mimeType: embeddedFile.mimeType, id: id, dataURL: embeddedFile.getImage(this.isDark) as DataURL, created: embeddedFile.mtime, size: embeddedFile.size, hasSVGwithBitmap: embeddedFile.isSVGwithBitmap, - shouldScale: embeddedFile.shouldScale() + shouldScale: embeddedFile.shouldScale(), + pdfPageViewProps: embeddedFile.pdfPageViewProps, }; files[batch].push(fileData); } @@ -803,7 +820,7 @@ export class EmbeddedFilesLoader { private async pdfToDataURL( file: TFile, linkParts: LinkParts, - ): Promise<[DataURL,{width:number, height:number}]> { + ): Promise<[DataURL,Size, PDFPageViewProps]> { try { let width = 0, height = 0; const pdfDoc = this.pdfDocsMap.get(file.path) ?? await getPDFDoc(file); @@ -814,6 +831,7 @@ export class EmbeddedFilesLoader { const scale = this.plugin.settings.pdfScale; const cropRect = linkParts.ref.split("rect=")[1]?.split(",").map(x=>parseInt(x)); const validRect = cropRect && cropRect.length === 4 && cropRect.every(x=>!isNaN(x)); + let viewProps: PDFPageViewProps; // Render the page const renderPage = async (num:number) => { @@ -824,8 +842,8 @@ export class EmbeddedFilesLoader { const page = await pdfDoc.getPage(num); // Set scale const viewport = page.getViewport({ scale }); - height = canvas.height = viewport.height; - width = canvas.width = viewport.width; + height = canvas.height = Math.round(viewport.height); + width = canvas.width = Math.round(viewport.width); const renderCtx = { canvasContext: ctx, @@ -846,9 +864,10 @@ export class EmbeddedFilesLoader { continue; } } + const [left, bottom, right, top] = page.view; + viewProps = {left, bottom, right, top}; + if(validRect) { - const [left, bottom, _, top] = page.view; - const pageHeight = top - bottom; width = (cropRect[2] - cropRect[0]) * scale; height = (cropRect[3] - cropRect[1]) * scale; @@ -868,19 +887,19 @@ export class EmbeddedFilesLoader { const canvas = await renderPage(pageNum); if(canvas) { - const result: [DataURL,{width:number, height:number}] = [`data:image/png;base64,${await new Promise((resolve, reject) => { + const result: [DataURL,Size, PDFPageViewProps] = [`data:image/png;base64,${await new Promise((resolve, reject) => { canvas.toBlob(async (blob) => { const dataURL = await blobToBase64(blob); resolve(dataURL); }); - })}` as DataURL, {width, height}]; + })}` as DataURL, {width, height}, viewProps]; canvas.width = 0; //free memory iOS bug canvas.height = 0; return result; } } catch(e) { console.log(e); - return [null,null]; + return [null, null, null]; } } diff --git a/src/shared/ExcalidrawAutomate.ts b/src/shared/ExcalidrawAutomate.ts index 29c861c..e80df45 100644 --- a/src/shared/ExcalidrawAutomate.ts +++ b/src/shared/ExcalidrawAutomate.ts @@ -54,6 +54,7 @@ import { scaleLoadedImage, wrapTextAtCharLength, arrayToMap, + addAppendUpdateCustomData, } from "src/utils/utils"; import { getAttachmentsFolderAndFilePath, getExcalidrawViews, getLeaf, getNewOrAdjacentLeaf, isObsidianThemeDark, mergeMarkdownFiles, openLeaf } from "src/utils/obsidianUtils"; import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types"; @@ -97,7 +98,6 @@ import { ExcalidrawLib } from "../types/excalidrawLib"; import { GlobalPoint } from "@zsviczian/excalidraw/types/math/types"; import { AddImageOptions, ImageInfo, SVGColorInfo } from "src/types/excalidrawAutomateTypes"; import { errorMessage, filterColorMap, getEmbeddedFileForImageElment, isColorStringTransparent, isSVGColorInfo, mergeColorMapIntoSVGColorInfo, svgColorInfoToColorMap, updateOrAddSVGColorInfo } from "src/utils/excalidrawAutomateUtils"; -import { Color } from "chroma-js"; extendPlugins([ HarmonyPlugin, @@ -141,6 +141,23 @@ export class ExcalidrawAutomate { this.plugin.printStarupBreakdown(); } + /** + * Add, modify keys in element customData and preserve existing keys. + * Creates customData={} if it does not exist. + * Takes the element ID for an element in the elementsDict and the new data to add or modify. + * To delete keys set key value in newData to undefined. so {keyToBeDeleted:undefined} will be deleted. + * @param id + * @param newData + * @returns undefined if element does not exist in elementsDict, returns the modified element otherwise. + */ + public addAppendUpdateCustomData(id:string, newData: Partial>) { + const el = this.elementsDict[id]; + if (!el) { + return; + } + return addAppendUpdateCustomData(el,newData); + } + public help(target: Function | string) { if (!target) { log("Usage: ea.help(ea.functionName) or ea.help('propertyName') or ea.help('utils.functionName') - notice property name and utils function name is in quotes"); @@ -1596,6 +1613,7 @@ export class ExcalidrawAutomate { width: image.size.width, }, colorMap, + pdfPageViewProps: image.pdfPageViewProps, }; if (scale && (Math.max(image.size.width, image.size.height) > MAX_IMAGE_SIZE)) { const scale = @@ -2392,14 +2410,14 @@ export class ExcalidrawAutomate { return false; } const elements = this.getElements(); - return await this.targetView.addElements( - elements, + return await this.targetView.addElements({ + newElements: elements, repositionToCursor, save, - this.imagesDict, + images: this.imagesDict, newElementsOnTop, shouldRestoreElements, - ); + }); }; /** diff --git a/src/shared/ExcalidrawData.ts b/src/shared/ExcalidrawData.ts index a4fc841..104900a 100644 --- a/src/shared/ExcalidrawData.ts +++ b/src/shared/ExcalidrawData.ts @@ -649,7 +649,6 @@ export class ExcalidrawData { containers.forEach((container: any) => { if(ellipseAndRhombusContainerWrapping && !container.customData?.legacyTextWrap) { addAppendUpdateCustomData(container, {legacyTextWrap: true}); - //container.customData = {...container.customData, legacyTextWrap: true}; } const filteredBoundElements = container.boundElements.filter( (boundEl: any) => elements.some((el: any) => el.id === boundEl.id), @@ -1569,13 +1568,13 @@ export class ExcalidrawData { filepath, ); - embeddedFile.setImage( - dataURL, + embeddedFile.setImage({ + imgBase64: dataURL, mimeType, - { height: 0, width: 0 }, - scene.appState?.theme === "dark", - mimeType === "image/svg+xml", //this treat all SVGs as if they had embedded images REF:addIMAGE - ); + size: { height: 0, width: 0 }, + isDark: scene.appState?.theme === "dark", + isSVGwithBitmap: mimeType === "image/svg+xml", //this treat all SVGs as if they had embedded images REF:addIMAGE + }); this.setFile(key as FileId, embeddedFile); return file; } @@ -1593,7 +1592,9 @@ export class ExcalidrawData { const pageRef = ef.linkParts.original.split("#")?.[1]; if(!pageRef || !pageRef.startsWith("page=") || pageRef.includes("rect")) return; const restOfLink = el.link ? el.link.match(/&rect=\d*,\d*,\d*,\d*(.*)/)?.[1] : ""; - const link = ef.linkParts.original + getPDFRect(el.crop, pdfScale) + (restOfLink ? restOfLink : "]]"); + const link = ef.linkParts.original + + getPDFRect({elCrop: el.crop, scale: pdfScale, customData: el.customData}) + + (restOfLink ? restOfLink : "]]"); el.link = `[[${link}`; this.elementLinks.set(el.id, el.link); dirty = true; @@ -1992,7 +1993,7 @@ export class ExcalidrawData { isLocalLink: data.isLocalLink, path: data.hyperlink, blockrefData: null, - hasSVGwithBitmap: data.isSVGwithBitmap + hasSVGwithBitmap: data.isSVGwithBitmap, }); return; } diff --git a/src/types/excalidrawAutomateTypes.ts b/src/types/excalidrawAutomateTypes.ts index eb93549..89a1c9a 100644 --- a/src/types/excalidrawAutomateTypes.ts +++ b/src/types/excalidrawAutomateTypes.ts @@ -1,7 +1,7 @@ import { DataURL } from "@zsviczian/excalidraw/types/excalidraw/types"; import { TFile } from "obsidian"; import { FileId } from "src/core"; -import { ColorMap, MimeType } from "src/shared/EmbeddedFileLoader"; +import { ColorMap, MimeType, PDFPageViewProps, Size } from "src/shared/EmbeddedFileLoader"; export type SVGColorInfo = Map +}): string { + const { left, bottom } = (customData && customData.pdfPageViewProps) + ? customData.pdfPageViewProps as PDFPageViewProps + : { left: 0, bottom: 0 }; + + const R0 = elCrop.x / scale + left; const R2 = elCrop.width / scale + R0; - const R3 = (elCrop.naturalHeight - elCrop.y) / scale; + const R3 = bottom + (elCrop.naturalHeight - elCrop.y) / scale; const R1 = R3 - elCrop.height / scale; return `&rect=${Math.round(R0)},${Math.round(R1)},${Math.round(R2)},${Math.round(R3)}`; } \ No newline at end of file diff --git a/src/utils/utils.ts b/src/utils/utils.ts index d1eaa9f..8db3ed3 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -18,7 +18,7 @@ import { getContainerElement, } from "../constants/constants"; import ExcalidrawPlugin from "../core/main"; -import { ExcalidrawElement, ExcalidrawTextElement, ImageCrop } from "@zsviczian/excalidraw/types/excalidraw/element/types"; +import { ExcalidrawElement, ExcalidrawImageElement, ExcalidrawTextElement, ImageCrop } from "@zsviczian/excalidraw/types/excalidraw/element/types"; import { ExportSettings } from "../view/ExcalidrawView"; import { getDataURLFromURL, getIMGFilename, getMimeType, getURLImageExtension } from "./fileUtils"; import { generateEmbeddableLink } from "./customEmbeddableUtils"; @@ -32,6 +32,7 @@ import { runCompressionWorker } from "src/shared/Workers/compression-worker"; import Pool from "es6-promise-pool"; import { FileData } from "../shared/EmbeddedFileLoader"; import { t } from "src/lang/helpers"; +import ExcalidrawScene from "src/shared/svgToExcalidraw/elements/ExcalidrawScene"; declare const PLUGIN_VERSION:string; declare var LZString: any; @@ -415,11 +416,17 @@ export async function getImageSize ( }); }; -export function addAppendUpdateCustomData (el: Mutable, newData: any): ExcalidrawElement { +export function addAppendUpdateCustomData ( + el: Mutable, + newData: Partial> +): ExcalidrawElement { if(!newData) return el; if(!el.customData) el.customData = {}; for (const key in newData) { - if(typeof newData[key] === "undefined") continue; + if(typeof newData[key] === "undefined") { + delete el.customData[key]; + continue; + } el.customData[key] = newData[key]; } return el; @@ -447,7 +454,7 @@ export function scaleLoadedImage ( scene.elements .filter((e: any) => e.type === "image" && e.fileId === img.id) - .forEach((el: any) => { + .forEach((el: Mutable) => { const [elWidth, elHeight] = [el.width, el.height]; const maintainArea = img.shouldScale; //true if image should maintain its area, false if image should display at 100% its size const elCrop: ImageCrop = el.crop; diff --git a/src/view/ExcalidrawView.ts b/src/view/ExcalidrawView.ts index 05640aa..e241b3d 100644 --- a/src/view/ExcalidrawView.ts +++ b/src/view/ExcalidrawView.ts @@ -105,8 +105,9 @@ import { shouldEmbedScene, _getContainerElement, arrayToMap, + addAppendUpdateCustomData, } from "../utils/utils"; -import { cleanBlockRef, cleanSectionHeading, closeLeafView, getAttachmentsFolderAndFilePath, getLeaf, getParentOfClass, obsidianPDFQuoteWithRef, openLeaf, setExcalidrawView } from "../utils/obsidianUtils"; +import { cleanBlockRef, cleanSectionHeading, closeLeafView, getActivePDFPageNumberFromPDFView, getAttachmentsFolderAndFilePath, getLeaf, getParentOfClass, obsidianPDFQuoteWithRef, openLeaf, setExcalidrawView } from "../utils/obsidianUtils"; import { splitFolderAndFilename } from "../utils/fileUtils"; import { ConfirmationPrompt, GenericInputPrompt, NewFileActions, Prompt, linkPrompt } from "../shared/Dialogs/Prompt"; import { ClipboardData } from "@zsviczian/excalidraw/types/excalidraw/clipboard"; @@ -147,6 +148,7 @@ import { IS_WORKER_SUPPORTED } from "../shared/Workers/compression-worker"; import { getPDFCropRect } from "../utils/PDFUtils"; import { Position, ViewSemaphores } from "../types/excalidrawViewTypes"; import { DropManager } from "./managers/DropManager"; +import { ImageInfo } from "src/types/excalidrawAutomateTypes"; const EMBEDDABLE_SEMAPHORE_TIMEOUT = 2000; const PREVENT_RELOAD_TIMEOUT = 2000; @@ -223,7 +225,9 @@ export const addFiles = async ( .filter((f:FileData) => view.excalidrawData.getFile(f.id)?.file?.extension === "pdf") .forEach((f:FileData) => { s.scene.elements - .filter((el:ExcalidrawElement)=>el.type === "image" && el.fileId === f.id && el.crop && el.crop.naturalWidth !== f.size.width) + .filter((el:ExcalidrawElement)=>el.type === "image" && el.fileId === f.id && ( + (el.crop && el.crop.naturalWidth !== f.size.width) || !el.customData?.pdfPageViewProps + )) .forEach((el:Mutable) => { s.dirty = true; const scale = f.size.width / el.crop.naturalWidth; @@ -235,6 +239,7 @@ export const addFiles = async ( naturalWidth: f.size.width, naturalHeight: f.size.height, }; + addAppendUpdateCustomData(el, { pdfPageViewProps: f.pdfPageViewProps}); }); }); @@ -250,13 +255,14 @@ export const addFiles = async ( if (view.excalidrawData.hasFile(f.id)) { const embeddedFile = view.excalidrawData.getFile(f.id); - embeddedFile.setImage( - f.dataURL, - f.mimeType, - f.size, + embeddedFile.setImage({ + imgBase64: f.dataURL, + mimeType: f.mimeType, + size: f.size, isDark, - f.hasSVGwithBitmap, - ); + isSVGwithBitmap: f.hasSVGwithBitmap, + pdfPageViewProps: f.pdfPageViewProps, + }); } if (view.excalidrawData.hasEquation(f.id)) { const latex = view.excalidrawData.getEquation(f.id).latex; @@ -1087,7 +1093,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{ isFullscreen(): boolean { //(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.isFullscreen, "ExcalidrawView.isFullscreen"); - return Boolean(document.body.querySelector(".excalidraw-hidden")); + return Boolean(this.ownerDocument.body.querySelector(".excalidraw-hidden")); } exitFullscreen() { @@ -3322,19 +3328,31 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{ const isPointerOutsideVisibleArea = top.x>this.currentPosition.x || bottom.xthis.currentPosition.y || bottom.y { + public async addElements({ + newElements, + repositionToCursor = false, + save = false, + images, + newElementsOnTop = false, + shouldRestoreElements = false, + }: { + newElements: ExcalidrawElement[]; + repositionToCursor?: boolean; + save?: boolean; + images?: {[key: FileId]: ImageInfo}; + newElementsOnTop?: boolean; + shouldRestoreElements?: boolean; + }): Promise { (process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.addElements, "ExcalidrawView.addElements", newElements, repositionToCursor, save, images, newElementsOnTop, shouldRestoreElements); const api = this.excalidrawAPI as ExcalidrawImperativeAPI; if (!api) { @@ -3391,40 +3409,38 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{ ? el.concat(newElements.filter((e) => !removeList.includes(e.id))) : newElements.filter((e) => !removeList.includes(e.id)).concat(el); - this.updateScene( - { - elements, - storeAction: "capture", - }, - shouldRestoreElements, - ); - - if (images && Object.keys(images).length >0) { - const files: BinaryFileData[] = []; - Object.keys(images).forEach((k) => { + const files: BinaryFileData[] = []; + if (images && Object.keys(images).length >0) { + Object.keys(images).forEach((k: FileId) => { files.push({ mimeType: images[k].mimeType, id: images[k].id, dataURL: images[k].dataURL, created: images[k].created, }); - if (images[k].file || images[k].isHyperLink || images[k].isLocalLink) { + if (images[k].file || images[k].isHyperLink) { //|| images[k].isLocalLink but isLocalLink was never passed const embeddedFile = new EmbeddedFile( this.plugin, this.file.path, - images[k].isHyperLink && !images[k].isLocalLink + images[k].isHyperLink //&& !images[k].isLocalLink local link is never passed to addElements ? images[k].hyperlink - : images[k].file, + : (typeof images[k].file === "string" ? images[k].file : images[k].file.path), ); const st: AppState = api.getAppState(); - embeddedFile.setImage( - images[k].dataURL, - images[k].mimeType, - images[k].size, - st.theme === "dark", - images[k].hasSVGwithBitmap, - ); + embeddedFile.setImage({ + imgBase64: images[k].dataURL, + mimeType: images[k].mimeType, + size: images[k].size, + isDark: st.theme === "dark", + isSVGwithBitmap: images[k].hasSVGwithBitmap, + pdfPageViewProps: images[k].pdfPageViewProps, + }); this.excalidrawData.setFile(images[k].id, embeddedFile); + if(images[k].pdfPageViewProps) { + elements.filter((e) => e.type === "image" && e.fileId === images[k].id).forEach((e) => { + addAppendUpdateCustomData(e, {pdfPageViewProps: images[k].pdfPageViewProps}); + }); + } } if (images[k].latex) { this.excalidrawData.setEquation(images[k].id, { @@ -3433,8 +3449,20 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{ }); } }); + } + + this.updateScene( + { + elements, + storeAction: "capture", + }, + shouldRestoreElements, + ); + + if(files.length > 0) { api.addFiles(files); } + api.updateContainerSize(api.getSceneElements().filter(el => newIds.includes(el.id)).filter(isContainer)); if (save) { await this.save(false); //preventReload=false will ensure that markdown links are paresed and displayed correctly @@ -3993,7 +4021,9 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{ link, naturalHeight: fd.size.height, naturalWidth: fd.size.width, + pdfPageViewProps: fd.pdfPageViewProps, }); + addAppendUpdateCustomData(el, {pdfPageViewProps: fd.pdfPageViewProps}); if(el.crop) { el.width = el.crop.width/this.plugin.settings.pdfScale; el.height = el.crop.height/this.plugin.settings.pdfScale;