From 36ead43102d9d04cd8fc34cf8479fc9c36ea5bea Mon Sep 17 00:00:00 2001 From: Zsolt Viczian Date: Sun, 28 Nov 2021 20:54:51 +0100 Subject: [PATCH] 1.4.14 --- manifest.json | 2 +- src/EmbeddedFileLoader.ts | 143 +++++++++++++++++++++++++++++++------- src/ExcalidrawAutomate.ts | 7 +- src/constants.ts | 2 +- src/lang/locale/en.ts | 6 ++ src/main.ts | 2 + src/settings.ts | 14 ++++ versions.json | 2 +- 8 files changed, 146 insertions(+), 32 deletions(-) diff --git a/manifest.json b/manifest.json index 66fbc3e..142eff0 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-excalidraw-plugin", "name": "Excalidraw", - "version": "1.4.13", + "version": "1.4.14", "minAppVersion": "0.12.16", "description": "An Obsidian plugin to edit and view Excalidraw drawings", "author": "Zsolt Viczian", diff --git a/src/EmbeddedFileLoader.ts b/src/EmbeddedFileLoader.ts index efbd3cf..ced0bf9 100644 --- a/src/EmbeddedFileLoader.ts +++ b/src/EmbeddedFileLoader.ts @@ -1,16 +1,14 @@ import { FileId } from "@zsviczian/excalidraw/types/element/types"; import { BinaryFileData, DataURL } from "@zsviczian/excalidraw/types/types"; -import { link } from "fs"; import { App, MarkdownRenderer, Notice, TFile } from "obsidian"; -import { CASCADIA_FONT, fileid, FRONTMATTER_KEY_CSS, FRONTMATTER_KEY_FONT, FRONTMATTER_KEY_FONTCOLOR, IMAGE_TYPES, nanoid, VIRGIL_FONT } from "./constants"; +import { CASCADIA_FONT, fileid, FRONTMATTER_KEY_FONT, FRONTMATTER_KEY_FONTCOLOR, FRONTMATTER_KEY_MD_STYLE, IMAGE_TYPES, nanoid, VIRGIL_FONT } from "./constants"; import { createSVG } from "./ExcalidrawAutomate"; import { ExcalidrawData, getTransclusion } from "./ExcalidrawData"; -import ExcalidrawView, { ExportSettings } from "./ExcalidrawView"; +import { ExportSettings } from "./ExcalidrawView"; import { t } from "./lang/helpers"; -import de from "./lang/locale/de"; import { tex2dataURL } from "./LaTeX"; import ExcalidrawPlugin from "./main"; -import {debug, errorlog, getImageSize, getLinkParts, LinkParts, svgToBase64 } from "./Utils"; +import {errorlog, getImageSize, getLinkParts, LinkParts, svgToBase64 } from "./Utils"; export declare type MimeType = "image/svg+xml" | "image/png" | "image/jpeg" | "image/gif" | "application/octet-stream"; export type FileData = BinaryFileData & { @@ -282,12 +280,14 @@ const getSVGData = async (app: App, file: TFile): Promise => { } const convertMarkdownToSVG = async (plugin: ExcalidrawPlugin, file: TFile, linkParts: LinkParts): Promise => { - - //const text = await plugin.app.vault.cachedRead(file); + //1. + //get the markdown text const [text,line] = await getTransclusion(linkParts,plugin.app,file); - const fileCache = plugin.app.metadataCache.getFileCache(file); + + //2. //get styles + const fileCache = plugin.app.metadataCache.getFileCache(file); let fontName:string; let fontDef:string; let font = plugin.settings.mdFont; @@ -313,31 +313,93 @@ const convertMarkdownToSVG = async (plugin: ExcalidrawPlugin, file: TFile, linkP } const fontColor = fileCache?.frontmatter ? fileCache.frontmatter[FRONTMATTER_KEY_FONTCOLOR] : plugin.settings.mdFontColor; - - //construct SVG - const div = createDiv(); - div.setAttribute("xmlns","http://www.w3.org/1999/xhtml"); - div.style.fontFamily = fontName; - if(fontColor) div.style.color = fontColor; - div.style.fontSize = "initial"; - await MarkdownRenderer.renderMarkdown(text,div,file.path,plugin); - div.querySelectorAll(":scope > *[class^='frontmatter']").forEach((el)=>div.removeChild(el)); - //brute force to swap to because links anyway don't work when the foreignObject is - //encapsulated in an img element. does not render with an underline, will. - const xml = (new XMLSerializer().serializeToString(div)).replaceAll("",""); - let svgStyle = ' width="'+linkParts.width+'px" height="100%"'; + + let style = fileCache?.frontmatter ? (fileCache.frontmatter[FRONTMATTER_KEY_MD_STYLE]??"") : ""; + let frontmatterCSSisAfile = false; + if(style && style!="") { + const f = plugin.app.metadataCache.getFirstLinkpathDest(style,file.path); + if(f) { + style = await plugin.app.vault.read(f); + frontmatterCSSisAfile = true; + } + } + if(!frontmatterCSSisAfile && plugin.settings.mdCSS && plugin.settings.mdCSS!="") { + const f = plugin.app.metadataCache.getFirstLinkpathDest(plugin.settings.mdCSS,file.path); + if(f) { + style += "\n"+await plugin.app.vault.read(f); + } + } + + //3. + //SVG helper functions + //the SVG will first have ~infinite height. After sizing this will be reduced + let svgStyle = ' width="'+linkParts.width+'px" height="100000"'; let foreignObjectStyle = ' width="'+linkParts.width+'px" height="100%"'; - const svg = () => '' + const svg = (xml:string,style?:string) => + '' + + (style?'':'') + '' + xml + ''; + + //4. + //create document div - this will be the contents of the foreign object + const mdDIV = createDiv(); + mdDIV.setAttribute("xmlns","http://www.w3.org/1999/xhtml"); + mdDIV.setAttribute("class","excalidraw-md-host"); + mdDIV.setAttribute("style",style); + mdDIV.style.fontFamily = fontName; + mdDIV.style.overflow = "auto"; + mdDIV.style.display = "block"; + if(fontColor) mdDIV.style.color = fontColor; + + await MarkdownRenderer.renderMarkdown(text,mdDIV,file.path,plugin); + mdDIV.querySelectorAll(":scope > *[class^='frontmatter']").forEach((el)=>mdDIV.removeChild(el)); + + //this is a brute force approach to replace anchors with spans for better formatting + //mdDIV.innerHTML = mdDIV.innerHTML.replaceAll("",""); + + + //5.1 + //get SVG size. + //First I need to create a fully self contained copy of the document to convert + //blank styles into inline styles using computedStyle + const iframeHost = document.body.createDiv(); + iframeHost.style.display = "none"; + const iframe = iframeHost.createEl("iframe"); + const iframeDoc = iframe.contentWindow.document; + if(style) { + const styleEl = iframeDoc.createElement("style"); + styleEl.type = "text/css"; + styleEl.innerHTML = style; + iframeDoc.head.appendChild(styleEl); + } + const stylingDIV = iframeDoc.importNode(mdDIV,true) + iframeDoc.body.appendChild(stylingDIV); + + iframeDoc.body.querySelectorAll("*").forEach((el:HTMLElement)=>{ + const elementStyle = el.style; + const computedStyle = window.getComputedStyle(el); + let style = ""; + for (const prop in elementStyle) { + if (elementStyle.hasOwnProperty(prop)) { + style += prop + ": " + computedStyle[prop] + ";"; + } + } + el.setAttribute("style",style); + }); + + const xmlINiframe = (new XMLSerializer().serializeToString(stylingDIV)) + document.body.removeChild(iframeHost); + + //5.2 //get SVG size const parser = new DOMParser(); - const doc = parser.parseFromString(svg(),"image/svg+xml"); + const doc = parser.parseFromString(svg(xmlINiframe),"image/svg+xml"); const svgEl = doc.firstElementChild; const host = createDiv(); host.appendChild(svgEl); @@ -349,7 +411,40 @@ const convertMarkdownToSVG = async (plugin: ExcalidrawPlugin, file: TFile, linkP //finalize SVG svgStyle = ' width="'+linkParts.width+'px" height="'+svgHeight+'px"'; foreignObjectStyle = ' width="'+linkParts.width+'px" height="'+svgHeight+'px"'; - return svgToBase64(svg()) as DataURL; + const xml = (new XMLSerializer().serializeToString(mdDIV)) + const finalSVG = svg(xml,style); + plugin.ea.mostRecentMarkdownSVG = parser.parseFromString(finalSVG,"image/svg+xml").firstElementChild as SVGSVGElement; + return svgToBase64(finalSVG) as DataURL; +} + +const styleSandbox = async (style:string,fontName:string,fontColor:string, text:string,path:string,plugin:ExcalidrawPlugin) => { + const host = document.body.createDiv(); + host.style.display = "none"; + const iframe = host.createEl("iframe"); + const doc = iframe.contentWindow.document; + if(style) { + const styleEl = doc.createElement("style"); + styleEl.type = "text/css"; + styleEl.innerHTML = style; + doc.head.appendChild(styleEl); + } + const div = createDiv("div"); + + doc.body.querySelectorAll("*").forEach((el:HTMLElement)=>{ + const elementStyle = el.style; + const computedStyle = window.getComputedStyle(el); + let style = ""; + for (const prop in elementStyle) { + if (elementStyle.hasOwnProperty(prop)) { + style += prop + ": " + computedStyle[prop] + ";"; + } + } + el.setAttribute("style",style); + }); + + const xml = (new XMLSerializer().serializeToString(div)) + document.body.removeChild(host); + return xml; } const getDataURL = async (file: ArrayBuffer,mimeType: string): Promise => { diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index 2d8f334..e1e4de7 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -7,7 +7,6 @@ import { FileId, } from "@zsviczian/excalidraw/types/element/types"; import { - MarkdownRenderer, normalizePath, TFile } from "obsidian" @@ -169,7 +168,7 @@ export interface ExcalidrawAutomate { view: ExcalidrawView, //the excalidraw view receiving the drop pointerPosition: {x:number, y:number} //the pointer position on canvas at the time of drop }):boolean; - renderMarkdown(text:string,el:HTMLElement):Promise; + mostRecentMarkdownSVG:SVGSVGElement; //Markdown renderer will drop a copy of the most recent SVG here for debugging purposes } declare let window: any; @@ -750,9 +749,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin):Promise { - await MarkdownRenderer.renderMarkdown(text,el,'',this.plugin); - } + mostRecentMarkdownSVG:null, }; await initFonts(); return window.ExcalidrawAutomate; diff --git a/src/constants.ts b/src/constants.ts index 8547949..3854c14 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -15,7 +15,7 @@ export const FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS = "excalidraw-link-brackets"; export const FRONTMATTER_KEY_DEFAULT_MODE = "excalidraw-default-mode"; export const FRONTMATTER_KEY_FONT = "excalidraw-font"; export const FRONTMATTER_KEY_FONTCOLOR = "excalidraw-font-color"; -export const FRONTMATTER_KEY_CSS = "excalidraw-css-file"; +export const FRONTMATTER_KEY_MD_STYLE = "excalidraw-css"; export const VIEW_TYPE_EXCALIDRAW = "excalidraw"; export const ICON_NAME = "excalidraw-icon"; export const MAX_COLORS = 5; diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index fe78483..3cc874d 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -143,6 +143,12 @@ export default { MD_DEFAULT_COLOR_NAME: "The default font color to use for embedded markdown files.", MD_DEFAULT_COLOR_DESC: 'Set this to allowed css color names e.g. "steelblue" (https://www.w3schools.com/colors/colors_names.asp), or a valid hexadecimal color e.g. "#e67700". ' + 'You can override this setting by adding the following frontmatter-key to the embedded markdown file: "excalidraw-font-color: color_name_or_rgbhex"', + MD_CSS_NAME: "CSS file", + MD_CSS_DESC: "Filename of the CSS to apply to markdown embeds. Provide the filename with extension (e.g. 'md-embed.css'). Nota bene, the filename may also be a plain " + + "markdown file as well, just make sure the content is written using valid css syntax (e.g. 'md-embed-css.md') will work just as well. " + + "The generated HTML that is embedded into the image is the same as normal rendered documents in Obsidian. " + + "Setting the font-family in the css is currently not supported; it should be set separately using the setting above. " + + 'You can override this css setting by adding the following frontmatter-key to the embedded markdown file: "excalidraw-css: css_file_in_valut".', EMBED_HEAD: "Embed & Export", EMBED_PREVIEW_SVG_NAME: "Display SVG in markdown preview", EMBED_PREVIEW_SVG_DESC: "The default is to display drawings as SVG images in the markdown preview. Turning this feature off, the markdown preview will display the drawing as an embedded PNG image.", diff --git a/src/main.ts b/src/main.ts index 3571f34..6bf9167 100644 --- a/src/main.ts +++ b/src/main.ts @@ -460,6 +460,8 @@ export default class ExcalidrawPlugin extends Plugin { if(!this.settings.matchThemeTrigger) return; //@ts-ignore if(m[0]?.oldValue === m[0]?.target?.getAttribute("class")) return; + //@ts-ignore + if(m[0]?.oldValue?.includes("theme-dark") === m[0]?.target?.classList?.contains("theme-dark")) return; const theme = isObsidianThemeDark() ? "dark":"light"; const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); leaves.forEach((leaf:WorkspaceLeaf)=> { diff --git a/src/settings.ts b/src/settings.ts index 7726063..2f7f796 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -56,6 +56,7 @@ export interface ExcalidrawSettings { mdSVGmaxHeight: number, mdFont: string, mdFontColor: string, + mdCSS: string, } export const DEFAULT_SETTINGS: ExcalidrawSettings = { @@ -109,6 +110,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = { mdSVGmaxHeight: 800, mdFont: "Virgil", mdFontColor: "Black", + mdCSS: "" } export class ExcalidrawSettingTab extends PluginSettingTab { @@ -478,6 +480,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab { this.applySettingsUpdate(true); })); + new Setting(containerEl) + .setName(t("MD_CSS_NAME")) + .setDesc(t("MD_CSS_DESC")) + .addText(text => text + .setPlaceholder("filename of css file in vault") + .setValue(this.plugin.settings.mdCSS) + .onChange((value) => { + this.requestReloadDrawings=true; + this.plugin.settings.mdCSS = value; + this.applySettingsUpdate(true); + })); + this.containerEl.createEl('h1', {text: t("EMBED_HEAD")}); diff --git a/versions.json b/versions.json index fe9b7cb..d855f07 100644 --- a/versions.json +++ b/versions.json @@ -1,4 +1,4 @@ { - "1.4.13": "0.12.16", + "1.4.14": "0.12.16", "1.4.2": "0.11.13" }