Image click navigation. Image embeds finalized.

This commit is contained in:
Zsolt Viczian
2021-10-23 14:32:05 +02:00
parent c88c898f4a
commit f4a458061a
7 changed files with 211 additions and 136 deletions

View File

@@ -4,9 +4,9 @@
[x] update code to adopt change files moving from AppState to App
- Add "files" to legacy excalidraw export
[ ] PNG preview
[ ] markdown embed SVG 190
[ ] markdown embed PNG
[x] PNG preview
[x] markdown embed SVG 190
[x] markdown embed PNG
[ ] embed Excalidraw into other Excalidraw

View File

@@ -9,8 +9,8 @@ import {
normalizePath,
TFile
} from "obsidian"
import ExcalidrawView from "./ExcalidrawView";
import { getJSON, getSVGString } from "./ExcalidrawData";
import ExcalidrawView, { TextMode } from "./ExcalidrawView";
import { ExcalidrawData, getJSON, getSVGString } from "./ExcalidrawData";
import {
FRONTMATTER,
nanoid,
@@ -18,7 +18,7 @@ import {
VIEW_TYPE_EXCALIDRAW,
MAX_IMAGE_SIZE
} from "./constants";
import { generateSVGString, getObsidianImage, getPNG, getSVG, wrapText } from "./Utils";
import { embedFontsInSVG, generateSVGString, getObsidianImage, getPNG, getSVG, loadSceneFiles, svgToBase64, wrapText } from "./Utils";
import { AppState } from "@zsviczian/excalidraw/types/types";
declare type ConnectionPoint = "top"|"bottom"|"left"|"right";
@@ -283,7 +283,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
}
}
):Promise<string> {
const template = params?.templatePath ? (await getTemplate(params.templatePath)) : null;
const template = params?.templatePath ? (await getTemplate(params.templatePath,true)) : null;
let elements = template ? template.elements : [];
elements = elements.concat(this.getElements());
let frontmatter:string;
@@ -325,22 +325,24 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
currentItemEndArrowhead: template?.appState?.currentItemEndArrowhead ?? this.style.endArrowHead,
currentItemLinearStrokeSharpness: template?.appState?.currentItemLinearStrokeSharpness ?? this.style.strokeSharpness,
gridSize: template?.appState?.gridSize ?? this.canvas.gridSize,
files: template?.appState?.files ?? {},
}
},
files: template?.files ?? {},
};
return plugin.createDrawing(
params?.filename ? params.filename + '.excalidraw.md' : this.plugin.getNextDefaultFilename(),
params?.onNewPane ? params.onNewPane : false,
params?.foldername ? params.foldername : this.plugin.settings.folder,
frontmatter + await plugin.exportSceneToMD(
JSON.stringify(scene,null,"\t"))
this.plugin.settings.compatibilityMode
? JSON.stringify(scene,null,"\t")
: frontmatter + await plugin.exportSceneToMD(JSON.stringify(scene,null,"\t"))
);
},
async createSVG(templatePath?:string,embedFont:boolean = false):Promise<SVGSVGElement> {
const template = templatePath ? (await getTemplate(templatePath)) : null;
const automateElements = this.getElements();
const template = templatePath ? (await getTemplate(templatePath,true)) : null;
let elements = template ? template.elements : [];
elements = elements.concat(this.getElements());
elements = elements.concat(automateElements);
const svg = await getSVG(
{//createDrawing
type: "excalidraw",
@@ -350,20 +352,21 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
appState: {
theme: template?.appState?.theme ?? this.canvas.theme,
viewBackgroundColor: template?.appState?.viewBackgroundColor ?? this.canvas.viewBackgroundColor,
files: template?.appState?.files ?? {}
}
},
files: template?.files ?? {}
},
{
withBackground: plugin.settings.exportWithBackground,
withTheme: plugin.settings.exportWithTheme
}
)
return embedFont ? ExcalidrawView.embedFontsInSVG(svg) : svg;
return embedFont ? embedFontsInSVG(svg) : svg;
},
async createPNG(templatePath?:string, scale:number=1) {
const template = templatePath ? (await getTemplate(templatePath)) : null;
const automateElements = this.getElements();
const template = templatePath ? (await getTemplate(templatePath,true)) : null;
let elements = template ? template.elements : [];
elements = elements.concat(this.getElements());
elements = elements.concat(automateElements);
return getPNG(
{
type: "excalidraw",
@@ -373,8 +376,8 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
appState: {
theme: template?.appState?.theme ?? this.canvas.theme,
viewBackgroundColor: template?.appState?.viewBackgroundColor ?? this.canvas.viewBackgroundColor,
files: template?.appState?.files ?? {}
}
},
files: template?.files ?? {}
},
{
withBackground: plugin.settings.exportWithBackground,
@@ -824,21 +827,40 @@ async function getTemplate(fileWithPath:string, loadFiles:boolean = false):Promi
const vault = app.vault;
const file = app.metadataCache.getFirstLinkpathDest(normalizePath(fileWithPath),'');
if(file && file instanceof TFile) {
const data = await vault.read(file);
const data = (await vault.read(file)).replaceAll("\r\n","\n").replaceAll("\r","\n");
const excalidrawData:ExcalidrawData = new ExcalidrawData(window.ExcalidrawAutomate.plugin);
if(file.extension === "excalidraw") {
await excalidrawData.loadLegacyData(data,file);
return {
elements: excalidrawData.scene.elements,
appState: excalidrawData.scene.appState,
frontmatter: "",
files: excalidrawData.scene.files,
svgSnapshot: null,
};
}
const parsed = data.search("excalidraw-plugin: parsed\n")>-1 || data.search("excalidraw-plugin: locked\n")>-1; //locked for backward compatibility
await excalidrawData.loadData(data,file,parsed ? TextMode.parsed : TextMode.raw)
let trimLocation = data.search("# Text Elements\n");
if(trimLocation == -1) trimLocation = data.search("# Drawing\n");
const [scene,pos] = getJSON(data);
const svgSnapshot = getSVGString(data.substr(pos+scene.length));
if(loadFiles) {
await loadSceneFiles(app,excalidrawData.files,(fileArray:any)=>{
for(const f of fileArray) {
excalidrawData.scene.files[f.id] = f;
}
});
}
const excalidrawData = JSON_parse(scene);
return {
elements: excalidrawData.elements,
appState: excalidrawData.appState,
elements: excalidrawData.scene.elements,
appState: excalidrawData.scene.appState,
frontmatter: data.substring(0,trimLocation),
files: null,
svgSnapshot: svgSnapshot
files: excalidrawData.scene.files,
svgSnapshot: excalidrawData.svgSnapshot
};
};
return {

View File

@@ -87,7 +87,7 @@ export function getSVGString(data:string):string {
}
export class ExcalidrawData {
public svgString: string = null;
public svgSnapshot: string = null;
private textElements:Map<string,{raw:string, parsed:string}> = null;
public scene:any = null;
private file:TFile = null;
@@ -99,6 +99,7 @@ export class ExcalidrawData {
private plugin: ExcalidrawPlugin;
public loaded: boolean = false;
public files:Map<FileId,string> = null; //fileId, path
private compatibilityMode:boolean = false;
constructor(plugin: ExcalidrawPlugin) {
this.plugin = plugin;
@@ -116,6 +117,7 @@ export class ExcalidrawData {
this.file = file;
this.textElements = new Map<string,{raw:string, parsed:string}>();
this.files.clear();
this.compatibilityMode = false;
//I am storing these because if the settings change while a drawing is open parsing will run into errors during save
//The drawing will use these values until next drawing is loaded or this drawing is re-loaded
@@ -151,7 +153,7 @@ export class ExcalidrawData {
this.scene.files = {}; //loading legacy scenes that do not yet have the files attribute.
}
this.svgString = getSVGString(data.substr(pos+scene.length));
this.svgSnapshot = getSVGString(data.substr(pos+scene.length));
data = data.substring(0,pos);
@@ -203,6 +205,7 @@ export class ExcalidrawData {
}
public async loadLegacyData(data: string,file: TFile):Promise<boolean> {
this.compatibilityMode = true;
this.file = file;
this.textElements = new Map<string,{raw:string, parsed:string}>();
this.setShowLinkBrackets();
@@ -485,7 +488,7 @@ export class ExcalidrawData {
}
outString += '\n';
}
return outString + this.plugin.getMarkdownDrawingSection(JSON.stringify(this.scene,null,"\t"),this.svgString);
return outString + this.plugin.getMarkdownDrawingSection(JSON.stringify(this.scene,null,"\t"),this.svgSnapshot);
}
private async syncFiles(scene:SceneDataWithFiles):Promise<boolean> {
@@ -523,10 +526,12 @@ export class ExcalidrawData {
}
public async syncElements(newScene:any):Promise<boolean> {
//console.log("Excalidraw.Data.syncElements()");
let result = await this.syncFiles(newScene);
this.scene = newScene;//JSON_parse(newScene);
this.scene.files = {};
this.scene = newScene;
let result = false;
if(!this.compatibilityMode) {
result = await this.syncFiles(newScene);
this.scene.files = {};
}
result = result || this.setLinkPrefix() || this.setUrlPrefix() || this.setShowLinkBrackets();
await this.updateTextElementsFromScene();
return result || this.findNewTextElementsInScene();

View File

@@ -36,7 +36,7 @@ import ExcalidrawPlugin from './main';
import {ExcalidrawAutomate, repositionElementsToCursor} from './ExcalidrawAutomate';
import { t } from "./lang/helpers";
import { ExcalidrawData, REG_LINKINDEX_HYPERLINK, REGEX_LINK } from "./ExcalidrawData";
import { checkAndCreateFolder, download, generateSVGString, getNewOrAdjacentLeaf, getNewUniqueFilepath, getObsidianImage, getPNG, getSVG, loadSceneFiles, rotatedDimensions, splitFolderAndFilename, svgToBase64, viewportCoordsToSceneCoords } from "./Utils";
import { checkAndCreateFolder, download, embedFontsInSVG, generateSVGString, getNewOrAdjacentLeaf, getNewUniqueFilepath, getObsidianImage, getPNG, getSVG, loadSceneFiles, rotatedDimensions, splitFolderAndFilename, svgToBase64, viewportCoordsToSceneCoords } from "./Utils";
import { Prompt } from "./Prompt";
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
@@ -63,6 +63,7 @@ export default class ExcalidrawView extends TextFileView {
private getScene: Function = null;
public addElements: Function = null; //add elements to the active Excalidraw drawing
private getSelectedTextElement: Function = null;
private getSelectedImageElement: Function = null;
public addText:Function = null;
private refresh: Function = null;
public excalidrawRef: React.MutableRefObject<any> = null;
@@ -117,23 +118,12 @@ export default class ExcalidrawView extends TextFileView {
const svg = await getSVG(scene,exportSettings);
if(!svg) return;
let serializer =new XMLSerializer();
const svgString = serializer.serializeToString(ExcalidrawView.embedFontsInSVG(svg));
const svgString = serializer.serializeToString(embedFontsInSVG(svg));
if(file && file instanceof TFile) await this.app.vault.modify(file,svgString);
else await this.app.vault.create(filepath,svgString);
})();
}
public static embedFontsInSVG(svg:SVGSVGElement):SVGSVGElement {
//replace font references with base64 fonts
const includesVirgil = svg.querySelector("text[font-family^='Virgil']") != null;
const includesCascadia = svg.querySelector("text[font-family^='Cascadia']") != null;
const defs = svg.querySelector("defs");
if (defs && (includesCascadia || includesVirgil)) {
defs.innerHTML = "<style>" + (includesVirgil ? VIRGIL_FONT : "") + (includesCascadia ? CASCADIA_FONT : "")+"</style>";
}
return svg;
}
public savePNG(scene?: any) {
if(!scene) {
if (!this.getScene) return false;
@@ -168,7 +158,7 @@ export default class ExcalidrawView extends TextFileView {
await this.loadDrawing(false);
}
//generate SVG preview snapshot
this.excalidrawData.svgString = await generateSVGString(this.getScene(),this.plugin.settings);
this.excalidrawData.svgSnapshot = await generateSVGString(this.getScene(),this.plugin.settings);
}
await super.save();
}
@@ -180,12 +170,12 @@ export default class ExcalidrawView extends TextFileView {
//console.log("ExcalidrawView.getViewData()");
if(!this.getScene) return this.data;
if(!this.excalidrawData.loaded) return this.data;
const scene = this.getScene();
if(!this.compatibilityMode) {
let trimLocation = this.data.search(/(^%%\n)?# Text Elements\n/m);
if(trimLocation == -1) trimLocation = this.data.search(/(%%\n)?# Drawing\n/);
if(trimLocation == -1) return this.data;
const scene = this.excalidrawData.scene;
if(!this.autosaving) {
if(this.plugin.settings.autoexportSVG) this.saveSVG(scene);
if(this.plugin.settings.autoexportPNG) this.savePNG(scene);
@@ -197,7 +187,6 @@ export default class ExcalidrawView extends TextFileView {
return header + this.excalidrawData.generateMD();
}
if(this.compatibilityMode) {
const scene = this.excalidrawData.scene;
if(!this.autosaving) {
if(this.plugin.settings.autoexportSVG) this.saveSVG(scene);
if(this.plugin.settings.autoexportPNG) this.savePNG(scene);
@@ -208,62 +197,79 @@ export default class ExcalidrawView extends TextFileView {
}
async handleLinkClick(view: ExcalidrawView, ev:MouseEvent) {
let text:string = (this.textMode == TextMode.parsed)
? this.excalidrawData.getRawText(this.getSelectedTextElement().id)
: this.getSelectedTextElement().text;
if(!text) {
const selectedText = this.getSelectedTextElement();
let file = null;
let lineNum = 0;
let linkText:string = null;
if(selectedText?.id) {
linkText = (this.textMode == TextMode.parsed)
? this.excalidrawData.getRawText(selectedText.id)
: selectedText.text;
linkText = linkText.replaceAll("\n",""); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/187
if(linkText.match(REG_LINKINDEX_HYPERLINK)) {
window.open(linkText,"_blank");
return;
}
const parts = REGEX_LINK.getRes(linkText).next();
if(!parts.value) {
const tags = linkText.matchAll(/#([\p{Letter}\p{Emoji_Presentation}\p{Number}\/_-]+)/ug).next();
if(!tags.value || tags.value.length<2) {
new Notice(t("TEXT_ELEMENT_EMPTY"),4000);
return;
}
const search=this.app.workspace.getLeavesOfType("search");
if(search.length==0) return;
//@ts-ignore
search[0].view.setQuery("tag:"+tags.value[1]);
this.app.workspace.revealLeaf(search[0]);
if(document.fullscreenElement === this.contentEl) {
document.exitFullscreen();
this.zoomToFit();
}
return;
}
linkText = REGEX_LINK.getLink(parts);
if(linkText.match(REG_LINKINDEX_HYPERLINK)) {
window.open(linkText,"_blank");
return;
}
if(linkText.search("#")>-1) {
let t;
[t,lineNum] = await this.excalidrawData.getTransclusion(linkText);
linkText = linkText.substring(0,linkText.search("#"));
}
if(linkText.match(REG_LINKINDEX_INVALIDCHARS)) {
new Notice(t("FILENAME_INVALID_CHARS"),4000);
return;
}
file = view.app.metadataCache.getFirstLinkpathDest(linkText,view.file.path);
if (!ev.altKey && !file) {
new Notice(t("FILE_DOES_NOT_EXIST"), 4000);
return;
}
} else {
const selectedImage = this.getSelectedImageElement();
if(selectedImage?.id) {
await this.save(true); //in case pasted images haven't been saved yet
if(this.excalidrawData.files.has(selectedImage.fileId)) {
linkText = this.excalidrawData.files.get(selectedImage.fileId);
}
}
}
if(!linkText) {
new Notice(t("LINK_BUTTON_CLICK_NO_TEXT"),20000);
return;
}
text = text.replaceAll("\n",""); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/187
if(text.match(REG_LINKINDEX_HYPERLINK)) {
window.open(text,"_blank");
return;
}
const parts = REGEX_LINK.getRes(text).next();
if(!parts.value) {
const tags = text.matchAll(/#([\p{Letter}\p{Emoji_Presentation}\p{Number}\/_-]+)/ug).next();
if(!tags.value || tags.value.length<2) {
new Notice(t("TEXT_ELEMENT_EMPTY"),4000);
return;
}
const search=this.app.workspace.getLeavesOfType("search");
if(search.length==0) return;
//@ts-ignore
search[0].view.setQuery("tag:"+tags.value[1]);
this.app.workspace.revealLeaf(search[0]);
if(document.fullscreenElement === this.contentEl) {
document.exitFullscreen();
this.zoomToFit();
}
return;
}
text = REGEX_LINK.getLink(parts);
if(text.match(REG_LINKINDEX_HYPERLINK)) {
window.open(text,"_blank");
return;
}
let lineNum = null;
if(text.search("#")>-1) {
let t;
[t,lineNum] = await this.excalidrawData.getTransclusion(text);
text = text.substring(0,text.search("#"));
}
if(text.match(REG_LINKINDEX_INVALIDCHARS)) {
new Notice(t("FILENAME_INVALID_CHARS"),4000);
return;
}
const file = view.app.metadataCache.getFirstLinkpathDest(text,view.file.path);
if (!ev.altKey && !file) {
new Notice(t("FILE_DOES_NOT_EXIST"), 4000);
return;
}
try {
const f = view.file;
if(ev.shiftKey && document.fullscreenElement === this.contentEl) {
@@ -275,7 +281,7 @@ export default class ExcalidrawView extends TextFileView {
if(file) {
leaf.openFile(file,{eState: {line: lineNum-1}}); //if file exists open file and jump to reference
} else {
leaf.view.app.workspace.openLinkText(text,view.file.path);
leaf.view.app.workspace.openLinkText(linkText,view.file.path);
}
} catch (e) {
new Notice(e,4000);
@@ -362,6 +368,10 @@ export default class ExcalidrawView extends TextFileView {
this.preventReload = false;
return;
}
if(this.compatibilityMode) {
this.dirty = null;
return;
}
if(!this.excalidrawRef) return;
if(!this.file) return;
if(file) this.data = await this.app.vault.cachedRead(file);
@@ -383,7 +393,7 @@ export default class ExcalidrawView extends TextFileView {
data = this.data = data.replaceAll("\r\n","\n").replaceAll("\r","\n");
this.app.workspace.onLayoutReady(async ()=>{
this.dirty = null;
this.compatibilityMode = this.file.extension == "excalidraw";
this.compatibilityMode = this.file.extension === "excalidraw";
await this.plugin.loadSettings();
this.plugin.opencount++;
if(this.compatibilityMode) {
@@ -430,6 +440,7 @@ export default class ExcalidrawView extends TextFileView {
viewModeEnabled: viewModeEnabled,
... excalidrawData.appState,
},
files: excalidrawData.files,
commitToHistory: true,
});
if((this.app.workspace.activeLeaf === this.leaf) && this.excalidrawWrapperRef) {
@@ -440,30 +451,11 @@ export default class ExcalidrawView extends TextFileView {
this.instantiateExcalidraw({
elements: excalidrawData.elements,
appState: excalidrawData.appState,
files: excalidrawData.files,
libraryItems: await this.getLibrary(),
});
//files are loaded on excalidrawRef readyPromise
}
/*
//load files
this.excalidrawData.files.forEach((value,key)=> {
const file = this.app.vault.getAbstractFileByPath(value);
if(file && file instanceof TFile) {
getObsidianImage(this.plugin.app,file).then(async (data)=>{
if(!this.excalidrawData) return;
let files:BinaryFileData[] = [];
files.push({
mimeType : data.mimeType,
id: key as FileId,
dataURL: data.dataURL,
created: data.created
});
this.excalidrawAPI.addFiles(files);
});
}
});*/
}
}
//Compatibility mode with .excalidraw files
@@ -583,7 +575,7 @@ export default class ExcalidrawView extends TextFileView {
}
let svg = await getSVG(this.getScene(),exportSettings);
if(!svg) return null;
svg = ExcalidrawView.embedFontsInSVG(svg);
svg = embedFontsInSVG(svg);
download(null,svgToBase64(svg.outerHTML),this.file.basename+'.svg');
return;
}
@@ -671,7 +663,7 @@ export default class ExcalidrawView extends TextFileView {
if(this.excalidrawAPI.getAppState().viewModeEnabled) {
if(selectedTextElement) {
const retval = selectedTextElement;
selectedTextElement == null;
selectedTextElement = null;
return retval;
}
return {id:null,text:null};
@@ -690,6 +682,30 @@ export default class ExcalidrawView extends TextFileView {
return {id:selectedElement[0].id, text:selectedElement[0].text}; //return text element text
};
this.getSelectedImageElement = ():{id: string, fileId:string} => {
if(!excalidrawRef?.current) return {id:null,fileId:null};
if(this.excalidrawAPI.getAppState().viewModeEnabled) {
if(selectedImageElement) {
const retval = selectedImageElement;
selectedImageElement = null;
return retval;
}
return {id:null,fileId:null};
}
const selectedElement = this.excalidrawAPI.getSceneElements().filter((el:any)=>el.id==Object.keys(this.excalidrawAPI.getAppState().selectedElementIds)[0]);
if(selectedElement.length===0) return {id:null,fileId:null};
if(selectedElement[0].type == "image") return {id:selectedElement[0].id, fileId:selectedElement[0].fileId}; //an image element was selected. Return fileId
if(selectedElement[0].groupIds.length === 0) return {id:null,fileId:null}; //is the selected element part of a group?
const group = selectedElement[0].groupIds[0]; //if yes, take the first group it is part of
const imageElement = this
.excalidrawAPI
.getSceneElements()
.filter((el:any)=>el.groupIds?.includes(group))
.filter((el:any)=>el.type=="image"); //filter for Image elements of the group
if(imageElement.length===0) return {id:null,fileId:null}; //the group had no image element member
return {id:selectedElement[0].id, fileId:selectedElement[0].fileId}; //return image element fileId
};
this.addText = (text:string, fontFamily?:1|2|3) => {
if(!excalidrawRef?.current) {
return;
@@ -793,6 +809,7 @@ export default class ExcalidrawView extends TextFileView {
//variables used to handle click events in view mode
let selectedTextElement:{id:string,text:string} = null;
let selectedImageElement:{id:string,fileId:string} = null;
let timestamp = 0;
let blockOnMouseButtonDown = false;
@@ -822,6 +839,19 @@ export default class ExcalidrawView extends TextFileView {
//if there are still multiple text elements with links on top of each other, return the first
return {id:elementsWithLinks[0].id,text:elementsWithLinks[0].text};
}
const getImageElementAtPointer = (pointer:any) => {
const elements = this.excalidrawAPI.getSceneElements()
.filter((e:ExcalidrawElement)=>{
if (e.type !== "image") return false;
const [x,y,w,h] = rotatedDimensions(e);
return x<=pointer.x && x+w>=pointer.x
&& y<=pointer.y && y+h>=pointer.y;
});
if(elements.length==0) return null;
if(elements.length>1) return {id:elements[0].id,fileId:elements[0].fileId};
//if more than 1 image elements are at the location, return the first
}
let hoverPoint = {x:0,y:0};
let hoverPreviewTarget:EventTarget = null;
@@ -859,7 +889,13 @@ export default class ExcalidrawView extends TextFileView {
const event = new MouseEvent("click", {ctrlKey: true, shiftKey: this.shiftKeyDown, altKey:this.altKeyDown});
this.handleLinkClick(this,event);
selectedTextElement = null;
}
}
selectedImageElement = getImageElementAtPointer(currentPosition);
if(selectedImageElement) {
const event = new MouseEvent("click", {ctrlKey: true, shiftKey: this.shiftKeyDown, altKey:this.altKeyDown});
this.handleLinkClick(this,event);
selectedImageElement = null;
}
}
let mouseEvent:any = null;
@@ -930,7 +966,7 @@ export default class ExcalidrawView extends TextFileView {
//@ts-ignore
if(!(e.ctrlKey||e.metaKey)) return;
if(!(this.plugin.settings.allowCtrlClick)) return;
if(!this.getSelectedTextElement().id) return;
if(!(this.getSelectedTextElement().id || this.getSelectedImageElement().id)) return;
this.handleLinkClick(this,e);
},
onMouseMove: (e:MouseEvent) => {

View File

@@ -3,7 +3,7 @@ import { App, normalizePath, TAbstractFile, TFile, TFolder, Vault, WorkspaceLea
import { Random } from "roughjs/bin/math";
import { BinaryFileData, DataURL, Zoom } from "@zsviczian/excalidraw/types/types";
import { nanoid } from "nanoid";
import { IMAGE_TYPES } from "./constants";
import { CASCADIA_FONT, IMAGE_TYPES, VIRGIL_FONT } from "./constants";
import {ExcalidrawAutomate} from './ExcalidrawAutomate';
import ExcalidrawPlugin from "./main";
import { ExcalidrawElement, FileId } from "@zsviczian/excalidraw/types/element/types";
@@ -352,6 +352,18 @@ export const getPNG = async (scene:any, exportSettings:ExportSettings, scale:num
}
}
export const embedFontsInSVG = (svg:SVGSVGElement):SVGSVGElement => {
//replace font references with base64 fonts
const includesVirgil = svg.querySelector("text[font-family^='Virgil']") != null;
const includesCascadia = svg.querySelector("text[font-family^='Cascadia']") != null;
const defs = svg.querySelector("defs");
if (defs && (includesCascadia || includesVirgil)) {
defs.innerHTML = "<style>" + (includesVirgil ? VIRGIL_FONT : "") + (includesCascadia ? CASCADIA_FONT : "")+"</style>";
}
return svg;
}
export const loadSceneFiles = async (app:App, filesMap: Map<FileId, string>,addFiles:Function) => {
const entries = filesMap.entries();
let entry;

View File

@@ -32,10 +32,10 @@ export default {
SAVE_AS_SVG: "Save as SVG into Vault (CTRL/META+CLICK to export)",
OPEN_LINK: "Open selected text as link\n(SHIFT+CLICK to open in a new pane)",
EXPORT_EXCALIDRAW: "Export to an .Excalidraw file",
LINK_BUTTON_CLICK_NO_TEXT: 'Select a Text Element containing an internal or external link.\n'+
LINK_BUTTON_CLICK_NO_TEXT: 'Select a an ImageElement, or select a TextElement that contains an internal or external link.\n'+
'SHIFT CLICK this button to open the link in a new pane.\n'+
'CTRL/META CLICK the Text Element on the canvas has the same effect!',
TEXT_ELEMENT_EMPTY: "Text Element is empty, or [[valid-link|alias]] or [alias](valid-link) is not found",
'CTRL/META CLICK the Image or TextElement on the canvas has the same effect!',
TEXT_ELEMENT_EMPTY: "No ImageElement is selected or TextElement is empty, or [[valid-link|alias]] or [alias](valid-link) is not found",
FILENAME_INVALID_CHARS: 'File name cannot contain any of the following characters: * " \\  < > : | ?',
FILE_DOES_NOT_EXIST: "File does not exist. Hold down ALT (or ALT+SHIFT) and CLICK link button to create a new file.",
FORCE_SAVE: "Force-save to update transclusions in adjacent panes.\n(Please note, that autosave is always on)",

View File

@@ -56,7 +56,7 @@ import { Prompt } from "./Prompt";
import { around } from "monkey-around";
import { t } from "./lang/helpers";
import { MigrationPrompt } from "./MigrationPrompt";
import { checkAndCreateFolder, download, generateSVGString, getAttachmentsFolderAndFilePath, getIMGPathFromExcalidrawFile, getNewUniqueFilepath, getPNG, getSVG, splitFolderAndFilename, svgToBase64 } from "./Utils";
import { checkAndCreateFolder, download, embedFontsInSVG, generateSVGString, getAttachmentsFolderAndFilePath, getIMGPathFromExcalidrawFile, getNewUniqueFilepath, getPNG, getSVG, splitFolderAndFilename, svgToBase64 } from "./Utils";
declare module "obsidian" {
interface App {
@@ -250,7 +250,7 @@ export default class ExcalidrawPlugin extends Plugin {
svg = await getSVG(JSON_parse(scene),exportSettings);
}
if(!svg) return null;
svg = ExcalidrawView.embedFontsInSVG(svg);
svg = embedFontsInSVG(svg);
svg.removeAttribute('width');
svg.removeAttribute('height');
img.setAttribute("src",svgToBase64(svg.outerHTML));
@@ -789,7 +789,7 @@ export default class ExcalidrawPlugin extends Plugin {
const filename = file.name.substr(0,file.name.lastIndexOf(".excalidraw")) + (replaceExtension ? ".md" : ".excalidraw.md");
const fname = getNewUniqueFilepath(this.app.vault,filename,normalizePath(file.path.substr(0,file.path.lastIndexOf(file.name))));
console.log(fname);
const result = await this.app.vault.create(fname,FRONTMATTER + this.exportSceneToMD(data));
const result = await this.app.vault.create(fname,FRONTMATTER + await this.exportSceneToMD(data));
if (this.settings.keepInSync) {
['.svg','.png'].forEach( (ext:string)=>{
const oldIMGpath = file.path.substring(0,file.path.lastIndexOf(".excalidraw")) + ext;