This commit is contained in:
zsviczian
2024-01-10 22:12:02 +01:00
parent 8700405af8
commit 51cf3a9219
10 changed files with 65 additions and 19 deletions

View File

@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "2.0.16",
"version": "2.0.17",
"minAppVersion": "1.1.6",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",

View File

@@ -350,6 +350,7 @@ export class EmbeddedFilesLoader {
elements?: ExcalidrawElement[];
}) : Promise<{dataURL: DataURL, hasSVGwithBitmap:boolean}> {
//debug({where:"EmbeddedFileLoader.getExcalidrawSVG",uid:this.uid,file:file.name});
const isMask = isMaskFile(this.plugin, file);
const forceTheme = hasExportTheme(this.plugin, file)
? getExportTheme(this.plugin, file, "light")
: undefined;
@@ -358,7 +359,7 @@ export class EmbeddedFilesLoader {
? getWithBackground(this.plugin, file)
: false,
withTheme: !!forceTheme,
isMask: isMaskFile(this.plugin,file),
isMask,
};
const svg = replaceSVGColors(
await createSVG(

View File

@@ -682,7 +682,11 @@ export class ExcalidrawAutomate {
outString += `${key}: $$${item.latex}$$\n`;
} else {
if(item.file) {
outString += `${key}: [[${item.file}]]\n`;
if(item.file instanceof TFile) {
outString += `${key}: [[${item.file.path}]]\n`;
} else {
outString += `${key}: [[${item.file}]]\n`;
}
} else {
outString += `${key}: ${item.hyperlink}\n`;
}

View File

@@ -4014,9 +4014,9 @@ export default class ExcalidrawView extends TextFileView {
}
}
}
}
public getSingleSelectedImageWithURL(): {imageEl: ExcalidrawImageElement, embeddedFile: EmbeddedFile} {
public getSingleSelectedImage(): {imageEl: ExcalidrawImageElement, embeddedFile: EmbeddedFile} {
if(!this.excalidrawAPI) return null;
const els = this.getViewSelectedElements().filter(el=>el.type==="image");
if(els.length !== 1) {
@@ -4024,7 +4024,6 @@ export default class ExcalidrawView extends TextFileView {
}
const el = els[0] as ExcalidrawImageElement;
const imageFile = this.excalidrawData.getFile(el.fileId);
if(!imageFile?.isHyperLink) return null;
return {imageEl: el, embeddedFile: imageFile};
}
@@ -4163,19 +4162,38 @@ export default class ExcalidrawView extends TextFileView {
}
}
const img = this.getSingleSelectedImageWithURL();
if(img) {
const img = this.getSingleSelectedImage();
if(img && img.embeddedFile?.isHyperLink) {
contextMenuActions.push([
renderContextMenuAction(
t("CONVERT_URL_TO_FILE"),
() => {
this.convertImageElWithURLToLocalFile(img);
setTimeout(()=>this.convertImageElWithURLToLocalFile(img));
},
onClose
),
]);
}
if(img && img.embeddedFile && img.embeddedFile.mimeType === "image/svg+xml") {
contextMenuActions.push([
renderContextMenuAction(
t("IMPORT_SVG_CONTEXTMENU"),
() => {
const base64Content = img.embeddedFile.getImage(false).split(',')[1];
// Decoding the base64 content
const svg = atob(base64Content);
if(!svg || svg === "") return;
const ea = getEA(this) as ExcalidrawAutomate;
ea.importSVG(svg);
ea.addToGroup(ea.getElements().map(el=>el.id));
ea.addElementsToView(true, true, true,true);
},
onClose
),
]);
}
contextMenuActions.push([
renderContextMenuAction(
t("UNIVERSAL_ADD_FILE"),

View File

@@ -3,6 +3,8 @@ import { REG_LINKINDEX_INVALIDCHARS } from "../constants/constants";
import ExcalidrawView from "../ExcalidrawView";
import { t } from "../lang/helpers";
import ExcalidrawPlugin from "../main";
import { getEA } from "src";
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
export class ImportSVGDialog extends FuzzySuggestModal<TFile> {
public app: App;
@@ -38,12 +40,11 @@ export class ImportSVGDialog extends FuzzySuggestModal<TFile> {
async onChooseItem(item: TFile, event: KeyboardEvent): Promise<void> {
if(!item) return;
const ea = this.plugin.ea;
ea.reset();
ea.setView(this.view);
const ea = getEA(this.view) as ExcalidrawAutomate;
const svg = await app.vault.read(item);
if(!svg || svg === "") return;
ea.importSVG(svg);
ea.addToGroup(ea.getElements().map(el=>el.id));
ea.addElementsToView(true, true, true,true);
}

View File

@@ -17,6 +17,16 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://cdn.ko-fi.com/cdn/kofi3.png?v=3" height=45></a></div>
`,
"2.0.17":`
## Fixed
- Image cropping now supports dark mode
- Image cropping/carve out was not working reliably in some cases [#1546](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1546)
- Masking a mirrored image resulted in an off-positioned mask
## New
- Context menu action to convert SVG to Excalidraw strokes
- Updated Chinese translation (Thank you @tswwe)
`,
"2.0.16":`
## Fixed
- Image cropping did not work consistently with large image files on lower-powered devices [#1538](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1538).

View File

@@ -66,6 +66,7 @@ export default {
INSERT_COMMAND: "Insert Obsidian Command as a link",
INSERT_IMAGE: "Insert image or Excalidraw drawing from your vault",
IMPORT_SVG: "Import an SVG file as Excalidraw strokes (limited SVG support, TEXT currently not supported)",
IMPORT_SVG_CONTEXTMENU: "Convert SVG to strokes - with limitations",
INSERT_MD: "Insert markdown file from vault",
INSERT_PDF: "Insert PDF file from vault",
UNIVERSAL_ADD_FILE: "Insert ANY file",

View File

@@ -808,8 +808,8 @@ export default class ExcalidrawPlugin extends Plugin {
checkCallback: (checking: boolean) => {
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
if(!view) return false;
const img = view.getSingleSelectedImageWithURL();
if(!img) return false;
const img = view.getSingleSelectedImage();
if(!img || !img.embeddedFile?.isHyperLink) return false;
if(checking) return true;
view.convertImageElWithURLToLocalFile(img);
},

View File

@@ -25,6 +25,8 @@ export const carveOutImage = async (sourceEA: ExcalidrawAutomate, viewImageEl: E
newImage.y = 0;
newImage.width = width;
newImage.height = height;
const scale = newImage.scale;
newImage.scale = [1,1];
const ef = sourceEA.targetView.excalidrawData.getFile(viewImageEl.fileId);
let imageLink = "";
@@ -46,6 +48,7 @@ export const carveOutImage = async (sourceEA: ExcalidrawAutomate, viewImageEl: E
const file = await createImageCropperFile(targetEA, newImage.id, imageLink, foldername, filename);
if(!file) return;
//console.log(await app.vault.read(file));
sourceEA.clear();
sourceEA.copyViewElementsToEAforEditing([viewImageEl]);
const sourceImageEl = sourceEA.getElement(viewImageEl.id) as Mutable<ExcalidrawImageElement>;
@@ -55,6 +58,7 @@ export const carveOutImage = async (sourceEA: ExcalidrawAutomate, viewImageEl: E
const replacingImage = sourceEA.getElement(replacingImageID) as Mutable<ExcalidrawImageElement>;
replacingImage.width = sourceImageEl.width;
replacingImage.height = sourceImageEl.height;
replacingImage.scale = scale;
sourceEA.addElementsToView(false, true, true);
}
@@ -108,7 +112,7 @@ export const createImageCropperFile = async (targetEA: ExcalidrawAutomate, image
//wait for file to be created/indexed by Obsidian
let file = vault.getAbstractFileByPath(newPath);
let counter = 0;
while(!file && counter < 50) {
while((!file || !targetEA.isExcalidrawFile(file as TFile)) && counter < 50) {
await sleep(100);
file = vault.getAbstractFileByPath(newPath);
counter++;
@@ -119,6 +123,7 @@ export const createImageCropperFile = async (targetEA: ExcalidrawAutomate, image
return;
}
/*
//wait for the new ExcalidrawView to open and initialize
counter = 0;
let newView = workspace.getActiveViewOfType(ExcalidrawView) as ExcalidrawView;
@@ -151,7 +156,7 @@ export const createImageCropperFile = async (targetEA: ExcalidrawAutomate, image
new Notice("Image did not load to the view. NewExcalidraw Drawing is taking too long to load. Please try again.");
return;
}
*/
//console.log({counter, path: workspace.getActiveFile()?.path, newView, files: api.getFiles()});
return file;

View File

@@ -91,13 +91,17 @@ export class CropImage {
return {style, mask:maskSVG.innerHTML};
}
private async getImageSVG() {
private async getImage() {
const exportSettings:ExportSettings = {
withBackground: false,
withTheme: false,
isMask: false,
}
const images = Object.values(this.imageEA.imagesDict);
if(images.length === 1) {
return images[0].dataURL;
}
return await this.imageEA.createPNGBase64(null,1,exportSettings,null,null,0);
const imageSVG = await this.imageEA.createSVG(null,false,exportSettings,null,null,0);
const svgData = new XMLSerializer().serializeToString(imageSVG);
return `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svgData)))}`;
@@ -111,12 +115,14 @@ export class CropImage {
return;
}
const maskID = nanoid();
const imageID = nanoid();
const {viewBox, vbWidth, vbHeight, width, height} = this.getViewBoxAndSize();
const parser = new DOMParser();
const {style, mask} = await this.getMaskSVG();
const svgString = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="${viewBox}" width="${vbWidth}" height="${vbHeight}">\n` +
`<symbol id="${imageID}"><image width="100%" height="100%" href="${await this.getImage()}"/></symbol>\n` +
`<defs>${style}\n<mask id="${maskID}" x="0" y="0" width="${width}" height="${height}" maskUnits="userSpaceOnUse">\n${mask}\n</mask>\n</defs>\n` +
`<image x="0" y="0" width="${width}" height="${height}" mask="url(#${maskID})" mask-type="alpha" href="${await this.getImageSVG()}"/>\n</svg>`;
`<use x="0" y="0" width="${width}" height="${height}" mask="url(#${maskID})" mask-type="alpha" href="#${imageID}"/>\n</svg>`;
return parser.parseFromString(
svgString,
"image/svg+xml",