mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
18 Commits
printToPDF
...
2.8.2-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b9abb13d0 | ||
|
|
6dbae61212 | ||
|
|
75c65d61c5 | ||
|
|
37e0de41af | ||
|
|
57f9e43508 | ||
|
|
3b7f931f28 | ||
|
|
b37a7aad4f | ||
|
|
667ab31ed9 | ||
|
|
b15ddef7fe | ||
|
|
ef785e5fb0 | ||
|
|
15ba4146ac | ||
|
|
9956fd1756 | ||
|
|
b6f2161f1c | ||
|
|
22b8b1f707 | ||
|
|
98f6871caa | ||
|
|
aae588249a | ||
|
|
ef890d51e3 | ||
|
|
b0bc03437a |
@@ -38,7 +38,7 @@ async function getImageSize(src: string): Promise<{ height: number; width: numbe
|
||||
export async function tex2dataURL(
|
||||
tex: string,
|
||||
scale: number = 4,
|
||||
app?: any
|
||||
plugin?: any
|
||||
): Promise<{
|
||||
mimeType: string;
|
||||
fileId: FileId;
|
||||
@@ -50,9 +50,9 @@ export async function tex2dataURL(
|
||||
let output: SVG<unknown, unknown, unknown>;
|
||||
|
||||
if(!adaptor) {
|
||||
if (app) {
|
||||
const file = app.vault.getAbstractFileByPath("preamble.sty");
|
||||
preamble = file ? await app.vault.read(file) : null;
|
||||
if (plugin) {
|
||||
const file = plugin.app.vault.getAbstractFileByPath(plugin.settings.latexPreambleLocation || "preamble.sty");
|
||||
preamble = file ? await plugin.app.vault.read(file) : null;
|
||||
}
|
||||
adaptor = liteAdaptor();
|
||||
RegisterHTMLHandler(adaptor);
|
||||
@@ -69,10 +69,18 @@ export async function tex2dataURL(
|
||||
|
||||
try {
|
||||
const node = html.convert(
|
||||
preamble ? `${preamble}${tex}` : tex,
|
||||
preamble ? `${preamble}\n${tex}` : tex,
|
||||
{ display: true, scale }
|
||||
);
|
||||
const svg = new DOMParser().parseFromString(adaptor.innerHTML(node), "image/svg+xml").firstChild as SVGSVGElement;
|
||||
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2195
|
||||
//https://stackoverflow.com/a/77181931
|
||||
let styleNode = document.createElement('style');
|
||||
styleNode.setAttribute("type", "text/css");
|
||||
styleNode.appendChild(document.createTextNode(".mjx-solid { stroke-width: 80px; }"));
|
||||
svg.appendChild(styleNode);
|
||||
|
||||
if (svg) {
|
||||
if(svg.width.baseVal.valueInSpecifiedUnits < 2) {
|
||||
svg.width.baseVal.valueAsString = `${(svg.width.baseVal.valueInSpecifiedUnits+1).toFixed(3)}ex`;
|
||||
|
||||
@@ -89,8 +89,8 @@ Plugin settings are grouped into the following sections:
|
||||
- **Basic settings**: such as default folders to use.
|
||||
- **Saving**: compression and autosave timer.
|
||||
- **Filename**: configure the automatically created Excalidraw filename.
|
||||
- **Display**: settings that effect the handling of Excalidraw (e.g.: left-handed mode, theme settings, mouse wheel and pinch zoom settings, zoom to fit settings).
|
||||
- **Links and transclusions**: Settings that effect how links and embedded items behave on the Excalidraw canvas.
|
||||
- **Display**: settings that affect the handling of Excalidraw (e.g.: left-handed mode, theme settings, mouse wheel and pinch zoom settings, zoom to fit settings).
|
||||
- **Links and transclusions**: Settings that affect how links and embedded items behave on the Excalidraw canvas.
|
||||
- **Markdown-embed settings**: These settings control how markdown documents from your Vault embedded into Excalidraw drawings will behave.
|
||||
- **Embed & Export**: Settings that control how Excalidraw images are displayed when embedding them into markdown documents.
|
||||
- **Auto-export Settings**: You can configure Excalidraw to create a PNG or SVG copy of your drawing each time it gets saved.
|
||||
|
||||
@@ -21,7 +21,7 @@ The script will convert your drawing into a slideshow presentation.
|
||||
|
||||
```javascript
|
||||
*/
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.1.7")) {
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.8.0")) {
|
||||
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
|
||||
return;
|
||||
}
|
||||
@@ -46,6 +46,8 @@ const TRANSITION_DELAY = 1000; //maximum time for transition between slides in m
|
||||
const FRAME_SLEEP = 1; //milliseconds
|
||||
const EDIT_ZOOMOUT = 0.7; //70% of original slide zoom, set to a value between 1 and 0
|
||||
const FADE_LEVEL = 0.1; //opacity of the slideshow controls after fade delay (value between 0 and 1)
|
||||
const PRINT_SLIDE_WIDTH = 1920;
|
||||
const PRINT_SLIDE_HEIGHT = 1080;
|
||||
//using outerHTML because the SVG object returned by Obsidin is in the main workspace window
|
||||
//but excalidraw might be open in a popout window which has a different document object
|
||||
const SVG_COG = ea.obsidian.getIcon("lucide-settings").outerHTML;
|
||||
@@ -57,6 +59,7 @@ const SVG_MAXIMIZE = ea.obsidian.getIcon("lucide-maximize").outerHTML;
|
||||
const SVG_MINIMIZE = ea.obsidian.getIcon("lucide-minimize").outerHTML;
|
||||
const SVG_LASER_ON = ea.obsidian.getIcon("lucide-hand").outerHTML;
|
||||
const SVG_LASER_OFF = ea.obsidian.getIcon("lucide-wand").outerHTML;
|
||||
const SVG_PRINTER = ea.obsidian.getIcon("lucide-printer").outerHTML;
|
||||
|
||||
//-------------------------------
|
||||
//utility & convenience functions
|
||||
@@ -202,7 +205,7 @@ const gotoFullscreen = async () => {
|
||||
}
|
||||
await waitForExcalidrawResize();
|
||||
const layerUIWrapper = contentEl.querySelector(".layer-ui__wrapper");
|
||||
if(!layerUIWrapper.hasClass("excalidraw-hidden")) layerUIWrapper.addClass("excalidraw-hidden");
|
||||
if(!layerUIWrapper?.hasClass("excalidraw-hidden")) layerUIWrapper.addClass("excalidraw-hidden");
|
||||
if(toggleFullscreenButton) toggleFullscreenButton.innerHTML = SVG_MINIMIZE;
|
||||
resetControlPanelElPosition();
|
||||
isFullscreen = true;
|
||||
@@ -271,8 +274,8 @@ if(presentationPathType==="line") {
|
||||
//-----------------------------
|
||||
// scroll-to-location functions
|
||||
//-----------------------------
|
||||
const getNavigationRect = ({ x1, y1, x2, y2 }) => {
|
||||
const { width, height } = excalidrawAPI.getAppState();
|
||||
const getNavigationRect = ({ x1, y1, x2, y2, printDimensions }) => {
|
||||
const { width, height } = printDimensions ? printDimensions : excalidrawAPI.getAppState();
|
||||
const ratioX = width / Math.abs(x1 - x2);
|
||||
const ratioY = height / Math.abs(y1 - y2);
|
||||
let ratio = Math.min(Math.max(ratioX, ratioY), 30);
|
||||
@@ -534,6 +537,20 @@ const createPresentationNavigationPanel = () => {
|
||||
}
|
||||
});
|
||||
}
|
||||
if(ea.DEVICE.isDesktop) {
|
||||
el.createEl("button",{
|
||||
attr: {
|
||||
style: `
|
||||
margin-right: calc(var(--default-button-size)*0.25);`,
|
||||
title: `Print to PDF\nClick to print slides at ${PRINT_SLIDE_WIDTH}x${
|
||||
PRINT_SLIDE_HEIGHT}\nHold SHIFT to print the presentation as displayed`
|
||||
//${!presentationPathLineEl ? "\nHold ALT/OPT to clip frames":""}`
|
||||
}
|
||||
}, button => {
|
||||
button.innerHTML = SVG_PRINTER;
|
||||
button.onclick = (e) => printToPDF(e);
|
||||
});
|
||||
}
|
||||
el.createEl("button",{
|
||||
attr: {
|
||||
style: `
|
||||
@@ -542,7 +559,7 @@ const createPresentationNavigationPanel = () => {
|
||||
}
|
||||
}, button => {
|
||||
button.innerHTML = SVG_FINISH;
|
||||
button.onclick = () => exitPresentation()
|
||||
button.onclick = () => exitPresentation();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -741,6 +758,96 @@ const exitPresentation = async (openForEdit = false) => {
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// Print to PDF
|
||||
//--------------------------
|
||||
let notice;
|
||||
let noticeEl;
|
||||
function setSingleNotice(message) {
|
||||
if(noticeEl?.parentElement) {
|
||||
notice.setMessage(message);
|
||||
return;
|
||||
}
|
||||
notice = new Notice(message, 0);
|
||||
noticeEl = notice.containerEl ?? notice.noticeEl;
|
||||
}
|
||||
|
||||
function hideSingleNotice() {
|
||||
if(noticeEl?.parentElement) {
|
||||
notice.hide();
|
||||
}
|
||||
}
|
||||
|
||||
const translateToZero = ({ top, left, bottom, right }, padding) => {
|
||||
const {topX, topY, width, height} = ea.getBoundingBox(ea.getViewElements());
|
||||
const newTop = top - (topY - padding);
|
||||
const newLeft = left - (topX - padding);
|
||||
const newBottom = bottom - (topY - padding);
|
||||
const newRight = right - (topX - padding);
|
||||
|
||||
return {
|
||||
top: newTop,
|
||||
left: newLeft,
|
||||
bottom: newBottom,
|
||||
right: newRight,
|
||||
};
|
||||
}
|
||||
|
||||
const printToPDF = async (e) => {
|
||||
const slideWidth = e.shiftKey ? excalidrawAPI.getAppState().width : PRINT_SLIDE_WIDTH;
|
||||
const slideHeight = e.shiftKey ? excalidrawAPI.getAppState().height : PRINT_SLIDE_HEIGHT;
|
||||
//const shouldClipFrames = !presentationPathLineEl && e.altKey;
|
||||
const shouldClipFrames = false;
|
||||
//huge padding to ensure the HD window always fits the width
|
||||
//no padding if frames are clipped
|
||||
const padding = shouldClipFrames ? 0 : Math.round(Math.max(slideWidth,slideHeight)/2)+10;
|
||||
const st = ea.getExcalidrawAPI().getAppState();
|
||||
setSingleNotice("Generating image. This can take a longer time depending on the size of the image and speed of your device");
|
||||
const svg = await ea.createViewSVG({
|
||||
withBackground: true,
|
||||
theme: st.theme,
|
||||
frameRendering: { enabled: shouldClipFrames, name: false, outline: false, clip: shouldClipFrames },
|
||||
padding,
|
||||
selectedOnly: false,
|
||||
skipInliningFonts: false,
|
||||
embedScene: false,
|
||||
});
|
||||
const pages = [];
|
||||
for(i=0;i<slides.length;i++) {
|
||||
setSingleNotice(`Generating slide ${i+1}`);
|
||||
const s = slides[i];
|
||||
const { top, left, bottom, right } = translateToZero(
|
||||
getNavigationRect({
|
||||
...s,
|
||||
printDimensions: {width: slideWidth, height: slideHeight}
|
||||
}), padding
|
||||
);
|
||||
//always create the new SVG in the main Obsidian workspace (not the popout window, if present)
|
||||
const host = window.createDiv();
|
||||
host.innerHTML = svg.outerHTML;
|
||||
const clonedSVG = host.firstElementChild;
|
||||
const width = Math.abs(left-right);
|
||||
const height = Math.abs(top-bottom);
|
||||
clonedSVG.setAttribute("viewBox", `${left} ${top} ${width} ${height}`);
|
||||
clonedSVG.setAttribute("width", `${width}`);
|
||||
clonedSVG.setAttribute("height", `${height}`);
|
||||
pages.push(clonedSVG);
|
||||
}
|
||||
const bgColor = ea.getExcalidrawAPI().getAppState().viewBackgroundColor;
|
||||
setSingleNotice("Creating PDF Document");
|
||||
ea.createPDF({
|
||||
SVG: pages,
|
||||
scale: { fitToPage: true },
|
||||
pageProps: {
|
||||
dimensions: { width: slideWidth, height: slideHeight },
|
||||
backgroundColor: bgColor,
|
||||
margin: { left: 0, right: 0, top: 0, bottom: 0 },
|
||||
alignment: "center"
|
||||
},
|
||||
filename: ea.targetView.file.basename + ".pdf",
|
||||
}).then(()=>hideSingleNotice());
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// Start presentation or open presentation settings on double click
|
||||
//--------------------------
|
||||
@@ -768,7 +875,11 @@ const start = async () => {
|
||||
}
|
||||
|
||||
const timestamp = Date.now();
|
||||
if(window.ExcalidrawSlideshow && (window.ExcalidrawSlideshow.script === utils.scriptFile.path) && (timestamp - window.ExcalidrawSlideshow.timestamp <400) ) {
|
||||
if(
|
||||
window.ExcalidrawSlideshow &&
|
||||
(window.ExcalidrawSlideshow.script === utils.scriptFile.path) &&
|
||||
(timestamp - window.ExcalidrawSlideshow.timestamp <400)
|
||||
) {
|
||||
if(window.ExcalidrawSlideshowStartTimer) {
|
||||
window.clearTimeout(window.ExcalidrawSlideshowStartTimer);
|
||||
delete window.ExcalidrawSlideshowStartTimer;
|
||||
@@ -789,4 +900,4 @@ if(window.ExcalidrawSlideshow && (window.ExcalidrawSlideshow.script === utils.sc
|
||||
window.ExcalidrawSlideshow.slide[ea.targetView.file.path] = 0;
|
||||
|
||||
window.ExcalidrawSlideshowStartTimer = window.setTimeout(start,500);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.8.0-rc-1",
|
||||
"version": "2.8.2-beta-1",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.7.5",
|
||||
"version": "2.8.1",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zsviczian/colormaster": "^1.2.2",
|
||||
"@zsviczian/excalidraw": "0.17.6-27",
|
||||
"@zsviczian/excalidraw": "0.17.6-29",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clsx": "^2.0.0",
|
||||
"es6-promise-pool": "2.5.0",
|
||||
@@ -3336,9 +3336,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@zsviczian/excalidraw": {
|
||||
"version": "0.17.6-27",
|
||||
"resolved": "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.17.6-27.tgz",
|
||||
"integrity": "sha512-8E5pfMbKO80d9oXyApF43SCKaR0CcLhVHjiQYEuAXzS7v6XzrDHODsNg+HuN9kOiShXmmn/e1MKVawo7bmeuOQ==",
|
||||
"version": "0.17.6-29",
|
||||
"resolved": "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.17.6-29.tgz",
|
||||
"integrity": "sha512-4UpAk1JGlHacjr3pyIhBU6e5+tgSggSq0+FhHEYPbeahLUAhnLXnpSLZUpEXTaftO4xooXOOvObB6BG/cfjaRA==",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.2",
|
||||
"@excalidraw/random-username": "1.1.0",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zsviczian/excalidraw": "0.17.6-27",
|
||||
"@zsviczian/excalidraw": "0.17.6-30",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clsx": "^2.0.0",
|
||||
"@zsviczian/colormaster": "^1.2.2",
|
||||
|
||||
@@ -756,7 +756,6 @@ export class CommandManager {
|
||||
if (view) {
|
||||
if(!view.exportDialog) {
|
||||
view.exportDialog = new ExportDialog(this.plugin, view,view.file);
|
||||
view.exportDialog.createForm();
|
||||
}
|
||||
view.exportDialog.open();
|
||||
return true;
|
||||
|
||||
@@ -173,6 +173,7 @@ export interface ExcalidrawSettings {
|
||||
showNewVersionNotification: boolean;
|
||||
//mathjaxSourceURL: string;
|
||||
latexBoilerplate: string;
|
||||
latexPreambleLocation: string;
|
||||
taskboneEnabled: boolean;
|
||||
taskboneAPIkey: string;
|
||||
pinnedScripts: string[];
|
||||
@@ -348,6 +349,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
showNewVersionNotification: true,
|
||||
//mathjaxSourceURL: "https://cdn.jsdelivr.net/npm/mathjax@3.2.1/es5/tex-svg.js",
|
||||
latexBoilerplate: "\\color{blue}",
|
||||
latexPreambleLocation: "preamble.sty",
|
||||
taskboneEnabled: false,
|
||||
taskboneAPIkey: "",
|
||||
pinnedScripts: [],
|
||||
@@ -2616,6 +2618,19 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("LATEX_PREAMBLE_NAME"))
|
||||
.setDesc(fragWithHTML(t("LATEX_PREAMBLE_DESC")))
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("e.g.: preamble.sty")
|
||||
.setValue(this.plugin.settings.latexPreambleLocation)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.latexPreambleLocation = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("FILETYPE_NAME"))
|
||||
.setDesc(fragWithHTML(t("FILETYPE_DESC")))
|
||||
|
||||
@@ -722,6 +722,8 @@ FILENAME_HEAD: "Filename",
|
||||
"restart Obsidian after closing settings, for this change to take effect.",
|
||||
LATEX_DEFAULT_NAME: "Default LaTeX formula for new equations",
|
||||
LATEX_DEFAULT_DESC: "Leave empty if you don't want a default formula. You can add default formatting here such as <code>\\color{white}</code>.",
|
||||
LATEX_PREAMBLE_NAME: "LaTeX preamble file (CasE SEnSiTivE!)",
|
||||
LATEX_PREAMBLE_DESC: "Full filepath to the preamble file, leave empty for default. If the file doesn't exist this option will be ignored.<br><strong>Important:</strong> Requires obsidian reload after change to take effect!",
|
||||
NONSTANDARD_HEAD: "Non-Excalidraw.com supported features",
|
||||
NONSTANDARD_DESC: `These settings in the "Non-Excalidraw.com Supported Features" section provide customization options beyond the default Excalidraw.com features. These features are not available on excalidraw.com. When exporting the drawing to Excalidraw.com these features will appear different.
|
||||
You can configure the number of custom pens displayed next to the Obsidian Menu on the canvas, allowing you to choose from a range of options. Additionally, you can enable a local font option, which adds a local font to the list of fonts on the element properties panel for text elements. `,
|
||||
@@ -1063,6 +1065,8 @@ FILENAME_HEAD: "Filename",
|
||||
EXPORTDIALOG_PDF_PAPER_CUSTOM: "Custom color",
|
||||
EXPORTDIALOG_PDF_ALIGNMENT: "Position on Page",
|
||||
EXPORTDIALOG_PDF_ALIGN_CENTER: "Center",
|
||||
EXPORTDIALOG_PDF_ALIGN_CENTER_LEFT: "Center Left",
|
||||
EXPORTDIALOG_PDF_ALIGN_CENTER_RIGHT: "Center Right",
|
||||
EXPORTDIALOG_PDF_ALIGN_TOP_LEFT: "Top Left",
|
||||
EXPORTDIALOG_PDF_ALIGN_TOP_CENTER: "Top Center",
|
||||
EXPORTDIALOG_PDF_ALIGN_TOP_RIGHT: "Top Right",
|
||||
|
||||
@@ -70,6 +70,7 @@ export class ExportDialog extends Modal {
|
||||
this.margin = plugin.settings.pdfSettings.margin;
|
||||
|
||||
this.saveSettings = false;
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
@@ -97,7 +98,7 @@ export class ExportDialog extends Modal {
|
||||
this.dirty = this.saveSettings;
|
||||
}
|
||||
|
||||
async createForm() {
|
||||
createForm() {
|
||||
if(DEVICE.isDesktop) {
|
||||
// Create tab container
|
||||
const tabContainer = this.contentEl.createDiv("nav-buttons-container");
|
||||
@@ -391,7 +392,6 @@ export class ExportDialog extends Modal {
|
||||
});
|
||||
bPDFExport.onclick = () => {
|
||||
this.view.exportPDF(
|
||||
false,
|
||||
this.hasSelectedElements && this.exportSelectedOnly,
|
||||
this.pageSize,
|
||||
this.pageOrientation
|
||||
@@ -402,7 +402,7 @@ export class ExportDialog extends Modal {
|
||||
|
||||
public getPaperColor(): string {
|
||||
switch (this.paperColor) {
|
||||
case "white": return "#ffffff";
|
||||
case "white": return this.theme === "light" ? "#ffffff" : "#000000";
|
||||
case "scene": return this.api.getAppState().viewBackgroundColor;
|
||||
case "custom": return this.customPaperColor;
|
||||
default: return "#ffffff";
|
||||
|
||||
@@ -17,6 +17,95 @@ 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://storage.ko-fi.com/cdn/kofi6.png?v=6" border="0" alt="Buy Me a Coffee at ko-fi.com" height=45></a></div>
|
||||
`,
|
||||
"2.8.1":`
|
||||
## Fixed
|
||||
- Unable to open Excalidraw files after the 2.8.0 update. [#2235](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2235)
|
||||
`,
|
||||
"2.8.0":`
|
||||
<div class="excalidraw-videoWrapper"><div>
|
||||
<iframe src="https://www.youtube.com/embed/tWi5xTUTz7E" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
|
||||
## New
|
||||
- Updated "Export Image" dialog
|
||||
- 🚀 PDF Export option including tiling of images over multiple pages. Only available on desktop :(
|
||||
- SVG to clipboard
|
||||
- More granular setting for padding and scale
|
||||
- Slideshow script can now print slides to PDF (update script from script store)
|
||||
- Set local graph to show the links in the embeddable when it is activated/deactivated [#2200](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2200)
|
||||
|
||||
## Fixed
|
||||
- Fixed several LaTeX issues. 🙏 @Sintuz [#1631](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1631), [#2195](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2195), [#1842](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1842)
|
||||
- Fixed support for *.jfif and *.avif images [#2212](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2212)
|
||||
- PDF++ selection is not correctly showing after embedded into a drawing (for some specific files) [#2213](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2213)
|
||||
- iOS 18 can't upload image and library [#2182](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2182)
|
||||
- Image block references are broken in hover previews [#2218](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2218)
|
||||
- ⚠️ Note there is a known issue in Obsidian 1.8.2 ⚠️ affecting preview windows in Excalidraw. I received confirmation that this will be fixed in 1.8.3. For now, if hover previews are important to you, you can downgrade to Obsidian 1.8.1 [#2228](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2225)
|
||||
- Mobile elements panel and context menu are not scrollable [#2216](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2216)
|
||||
- "Local Font" menu disappears when opening a drawing in an Obsidian popout-window [#2205](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2205)
|
||||
|
||||
## Updates from Excalidraw.com
|
||||
- Pressing delete on a frame will only delete the children [#9011](https://github.com/excalidraw/excalidraw/pull/9011)
|
||||
- New crowfoot arrowheads and a new arrowhead picker [#8942](https://github.com/excalidraw/excalidraw/pull/8942)
|
||||
- Fixed some of the arrow binding issues [#9010](https://github.com/excalidraw/excalidraw/pull/9010), [#2209](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2209)
|
||||
- New context menu action: "Wrap selection in frame" [#9005](https://github.com/excalidraw/excalidraw/pull/9005)
|
||||
- Elbow arrow segment fixing and positioning [#8952](https://github.com/excalidraw/excalidraw/pull/8952)
|
||||
- When drag creating a new frame, do not add a partial group to it. When wrapping a selected partial group in a frame however, do add it to the wrapping frame. But such that it should be separated from the previous containing group. [#9014](https://github.com/excalidraw/excalidraw/pull/9014)
|
||||
|
||||
## New in ExcalidrawAutomate
|
||||
- New hook: ${String.fromCharCode(96)}onImageFileNameHook${String.fromCharCode(96)}. When set, this callback is triggered when a image is being saved in Excalidraw.
|
||||
- PDF export functions, paving the way for slideshow to export slides to PDF
|
||||
${String.fromCharCode(96,96,96)}ts
|
||||
/**
|
||||
* Returns the dimensions of a standard page size in pixels.
|
||||
*/
|
||||
function getPagePDFDimensions(
|
||||
pageSize: PageSize,
|
||||
orientation: PageOrientation
|
||||
): PageDimensions;
|
||||
|
||||
/**
|
||||
* Creates a PDF from the provided SVG elements with specified scaling and page properties.
|
||||
*/
|
||||
function createPDF(props: {
|
||||
SVG: SVGSVGElement[];
|
||||
scale?: PDFExportScale;
|
||||
pageProps?: PDFPageProperties;
|
||||
filename: string;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates an SVG representation of the current view.
|
||||
*/
|
||||
function createViewSVG(props : {
|
||||
withBackground?: boolean;
|
||||
theme?: "light" | "dark";
|
||||
frameRendering?: FrameRenderingOptions;
|
||||
padding?: number;
|
||||
selectedOnly?: boolean;
|
||||
skipInliningFonts?: boolean;
|
||||
embedScene?: boolean;
|
||||
}): Promise<SVGSVGElement>;
|
||||
|
||||
/**
|
||||
* If set, this callback is triggered when a image is being saved in Excalidraw.
|
||||
* You can use this callback to customize the naming and path of pasted images to avoid
|
||||
* default names like "Pasted image 123147170.png" being saved in the attachments folder,
|
||||
* and instead use more meaningful names based on the Excalidraw file or other criteria,
|
||||
* plus save the image in a different folder.
|
||||
*
|
||||
* If the function returns null or undefined, the normal Excalidraw operation will continue
|
||||
* with the excalidraw generated name and default path.
|
||||
* If a filepath is returned, that will be used. Include the full Vault filepath and filename
|
||||
* with the file extension.
|
||||
* The currentImageName is the name of the image generated by excalidraw or provided during paste.
|
||||
*/
|
||||
function onImageFilePathHook: (data: {
|
||||
currentImageName: string;
|
||||
drawingFilePath: string;
|
||||
}) => string = null;
|
||||
${String.fromCharCode(96,96,96)}
|
||||
`,
|
||||
"2.7.5":`
|
||||
## Fixed
|
||||
- PDF export scenario described in [#2184](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2184)
|
||||
|
||||
@@ -129,6 +129,8 @@ export class PDFExportSettingsComponent {
|
||||
dropdown
|
||||
.addOptions({
|
||||
"center": t("EXPORTDIALOG_PDF_ALIGN_CENTER"),
|
||||
"center-left": t("EXPORTDIALOG_PDF_ALIGN_CENTER_LEFT"),
|
||||
"center-right": t("EXPORTDIALOG_PDF_ALIGN_CENTER_RIGHT"),
|
||||
"top-left": t("EXPORTDIALOG_PDF_ALIGN_TOP_LEFT"),
|
||||
"top-center": t("EXPORTDIALOG_PDF_ALIGN_TOP_CENTER"),
|
||||
"top-right": t("EXPORTDIALOG_PDF_ALIGN_TOP_RIGHT"),
|
||||
|
||||
@@ -226,14 +226,14 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
},
|
||||
{
|
||||
field: "createPDF",
|
||||
code: "async createPDF({SVG: SVGSVGElement[], scale?: PDFExportScale, pageProps?: PDFPageProperties}): Promise<void>",
|
||||
desc: "",
|
||||
after: "Creates a PDF from the provided SVG elements with specified scaling and page properties.\n" +
|
||||
code: "async createPDF({SVG: SVGSVGElement[], scale?: PDFExportScale, pageProps?: PDFPageProperties, filename: string}): Promise<void>",
|
||||
desc: "Creates a PDF from the provided SVG elements with specified scaling and page properties.\n" +
|
||||
"\n" +
|
||||
"@param {Object} params - The parameters for creating the PDF.\n" +
|
||||
"@param {SVGSVGElement[]} params.SVG - An array of SVG elements to be included in the PDF. If multiple SVGs are provided, each will be added to a new page.\n" +
|
||||
"@param {PDFExportScale} [params.scale={ fitToPage: true, zoom: 1 }] - The scaling options for the SVG elements.\n" +
|
||||
"@param {PDFPageProperties} [params.pageProps] - The properties for the PDF pages.\n" +
|
||||
"@param {string} params.filename - The name of the PDF file to be created.\n" +
|
||||
"@returns {Promise<ArrayBuffer>} - A promise that resolves to an ArrayBuffer containing the PDF data.\n" +
|
||||
"\n" +
|
||||
"@typedef {Object} PDFExportScale\n" +
|
||||
@@ -244,10 +244,8 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
"@property {{width: number, height: number}} [dimensions] - The dimensions of the PDF pages in pixels. Use getPageDimensions to get standard page sizes.\n" +
|
||||
"@property {string} [backgroundColor] - The background color of the PDF pages.\n" +
|
||||
"@property {PDFMargin} margin - The margins of the PDF pages in pixels.\n" +
|
||||
"@property {PDFPageAlignment} alignment - The alignment of the SVG on the PDF pages.\n" +
|
||||
"\n" +
|
||||
"@example\n" +
|
||||
"const pdfData = await createPDF({\n" +
|
||||
"@property {PDFPageAlignment} alignment - The alignment of the SVG on the PDF pages.",
|
||||
after: "({\n" +
|
||||
" SVG: [svgElement1, svgElement2],\n" +
|
||||
" scale: { fitToPage: true },\n" +
|
||||
" pageProps: {\n" +
|
||||
@@ -255,9 +253,40 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
" backgroundColor: \"#ffffff\",\n" +
|
||||
" margin: { left: 20, right: 20, top: 20, bottom: 20 },\n" +
|
||||
" alignment: \"center\"\n" +
|
||||
" filename: \"myPDF.pdf\"\n" +
|
||||
" }\n" +
|
||||
"});",
|
||||
},
|
||||
{
|
||||
field: "createViewSVG",
|
||||
code: "async createViewSVG({withBackground?: boolean, theme?: 'light' | 'dark', frameRendering?: FrameRenderingOptions, padding?: number, selectedOnly?: boolean, skipInliningFonts?: boolean, embedScene?: boolean}): Promise<SVGSVGElement>",
|
||||
desc: "Creates an SVG representation of the current view with specified options.\n" +
|
||||
"\n" +
|
||||
"@param {Object} options - The options for creating the SVG.\n" +
|
||||
"@param {boolean} [options.withBackground=true] - Whether to include the background in the SVG.\n" +
|
||||
"@param {\"light\" | \"dark\"} [options.theme] - The theme to use for the SVG.\n" +
|
||||
"@param {FrameRenderingOptions} [options.frameRendering={enabled: true, name: true, outline: true, clip: true}] - The frame rendering options.\n" +
|
||||
"@param {number} [options.padding] - The padding to apply around the SVG.\n" +
|
||||
"@param {boolean} [options.selectedOnly=false] - Whether to include only the selected elements in the SVG.\n" +
|
||||
"@param {boolean} [options.skipInliningFonts=false] - Whether to skip inlining fonts in the SVG.\n" +
|
||||
"@param {boolean} [options.embedScene=false] - Whether to embed the scene in the SVG.\n" +
|
||||
"@returns {Promise<SVGSVGElement>} A promise that resolves to the SVG element.\n" +
|
||||
"\n" +
|
||||
"@typedef {Object} FrameRenderingOptions\n" +
|
||||
"@property {boolean} enabled - Whether frame rendering is enabled.\n" +
|
||||
"@property {boolean} name - Whether to include the name in the frame rendering.\n" +
|
||||
"@property {boolean} outline - Whether to include the outline in the frame rendering.\n" +
|
||||
"@property {boolean} clip - Whether to clip the frame rendering.\n",
|
||||
after: "({\n" +
|
||||
" withBackground: true,\n" +
|
||||
" theme: 'light',\n" +
|
||||
" frameRendering: { enabled: true, name: true, outline: true, clip: true },\n" +
|
||||
" padding: 10,\n" +
|
||||
" selectedOnly: false,\n" +
|
||||
" skipInliningFonts: false,\n" +
|
||||
" embedScene: false,\n" +
|
||||
"});",
|
||||
},
|
||||
{
|
||||
field: "getPagePDFDimensions",
|
||||
code: "getPagePDFDimensions(pageSize: PageSize, orientation: PageOrientation): PageDimensions",
|
||||
@@ -273,12 +302,8 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
"\n" +
|
||||
"@typedef {\"A0\" | \"A1\" | \"A2\" | \"A3\" | \"A4\" | \"A5\" | \"Letter\" | \"Legal\" | \"Tabloid\"} PageSize\n" +
|
||||
"\n" +
|
||||
"@typedef {\"portrait\" | \"landscape\"} PageOrientation\n" +
|
||||
"\n" +
|
||||
"@example\n" +
|
||||
"const dimensions = getPDFPageDimensions(\"A4\", \"portrait\");\n" +
|
||||
"console.log(dimensions); // { width: 595.28, height: 841.89 }",
|
||||
after: "",
|
||||
"@typedef {\"portrait\" | \"landscape\"} PageOrientation",
|
||||
after: "(\"A4\", \"portrait\");",
|
||||
},
|
||||
{
|
||||
field: "createPNG",
|
||||
|
||||
@@ -704,7 +704,7 @@ export class EmbeddedFilesLoader {
|
||||
}
|
||||
if (!excalidrawData.getEquation(id).isLoaded) {
|
||||
const latex = equation.latex;
|
||||
const data = await tex2dataURL(latex, 4, this.plugin.app);
|
||||
const data = await tex2dataURL(latex, 4, this.plugin);
|
||||
if (data) {
|
||||
const fileData = {
|
||||
mimeType: data.mimeType,
|
||||
|
||||
@@ -42,6 +42,8 @@ import {
|
||||
wrapTextAtCharLength,
|
||||
arrayToMap,
|
||||
addAppendUpdateCustomData,
|
||||
getSVG,
|
||||
getWithBackground,
|
||||
} 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";
|
||||
@@ -84,6 +86,7 @@ import { GlobalPoint } from "@zsviczian/excalidraw/types/math/types";
|
||||
import { AddImageOptions, ImageInfo, SVGColorInfo } from "src/types/excalidrawAutomateTypes";
|
||||
import { _measureText, cloneElement, createPNG, createSVG, errorMessage, filterColorMap, getEmbeddedFileForImageElment, getFontFamily, getLineBox, getTemplate, isColorStringTransparent, isSVGColorInfo, mergeColorMapIntoSVGColorInfo, normalizeLinePoints, repositionElementsToCursor, svgColorInfoToColorMap, updateOrAddSVGColorInfo, verifyMinimumPluginVersion } from "src/utils/excalidrawAutomateUtils";
|
||||
import { exportToPDF, getMarginValue, getPageDimensions, PageDimensions, PageOrientation, PageSize, PDFExportScale, PDFPageProperties } from "src/utils/exportUtils";
|
||||
import { FrameRenderingOptions } from "src/types/utilTypes";
|
||||
|
||||
extendPlugins([
|
||||
HarmonyPlugin,
|
||||
@@ -885,7 +888,7 @@ export class ExcalidrawAutomate {
|
||||
Object.keys(this.imagesDict).forEach((key: FileId)=> {
|
||||
const item = this.imagesDict[key];
|
||||
if(item.latex) {
|
||||
outString += `${key}: $$${item.latex}$$\n\n`;
|
||||
outString += `${key}: $$${item.latex.trim()}$$\n\n`;
|
||||
} else {
|
||||
if(item.file) {
|
||||
if(item.file instanceof TFile) {
|
||||
@@ -969,6 +972,7 @@ export class ExcalidrawAutomate {
|
||||
* margin: { left: 20, right: 20, top: 20, bottom: 20 },
|
||||
* alignment: "center",
|
||||
* }
|
||||
* filename: "example.pdf",
|
||||
* });
|
||||
*/
|
||||
async createPDF({
|
||||
@@ -1002,6 +1006,68 @@ export class ExcalidrawAutomate {
|
||||
await exportToPDF({SVG, scale, pageProps, filename});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an SVG representation of the current view.
|
||||
*
|
||||
* @param {Object} options - The options for creating the SVG.
|
||||
* @param {boolean} [options.withBackground=true] - Whether to include the background in the SVG.
|
||||
* @param {"light" | "dark"} [options.theme] - The theme to use for the SVG.
|
||||
* @param {FrameRenderingOptions} [options.frameRendering={enabled: true, name: true, outline: true, clip: true}] - The frame rendering options.
|
||||
* @param {number} [options.padding] - The padding to apply around the SVG.
|
||||
* @param {boolean} [options.selectedOnly=false] - Whether to include only the selected elements in the SVG.
|
||||
* @param {boolean} [options.skipInliningFonts=false] - Whether to skip inlining fonts in the SVG.
|
||||
* @param {boolean} [options.embedScene=false] - Whether to embed the scene in the SVG.
|
||||
* @returns {Promise<SVGSVGElement>} A promise that resolves to the SVG element.
|
||||
*/
|
||||
async createViewSVG({
|
||||
withBackground = true,
|
||||
theme,
|
||||
frameRendering = {enabled: true, name: true, outline: true, clip: true},
|
||||
padding,
|
||||
selectedOnly = false,
|
||||
skipInliningFonts = false,
|
||||
embedScene = false,
|
||||
} : {
|
||||
withBackground?: boolean,
|
||||
theme?: "light" | "dark",
|
||||
frameRendering?: FrameRenderingOptions,
|
||||
padding?: number,
|
||||
selectedOnly?: boolean,
|
||||
skipInliningFonts?: boolean,
|
||||
embedScene?: boolean,
|
||||
}): Promise<SVGSVGElement> {
|
||||
if(!this.targetView || !this.targetView.file || !this.targetView._loaded) {
|
||||
console.log("No view loaded");
|
||||
return;
|
||||
}
|
||||
const view = this.targetView;
|
||||
const scene = this.targetView.getScene(selectedOnly);
|
||||
|
||||
const exportSettings: ExportSettings = {
|
||||
withBackground: view.getViewExportWithBackground(withBackground),
|
||||
withTheme: true,
|
||||
isMask: isMaskFile(this.plugin, view.file),
|
||||
skipInliningFonts,
|
||||
frameRendering,
|
||||
};
|
||||
|
||||
return await getSVG(
|
||||
{
|
||||
...scene,
|
||||
...{
|
||||
appState: {
|
||||
...scene.appState,
|
||||
theme: view.getViewExportTheme(theme),
|
||||
exportEmbedScene: view.getViewExportEmbedScene(embedScene),
|
||||
},
|
||||
},
|
||||
},
|
||||
exportSettings,
|
||||
view.getViewExportPadding(padding),
|
||||
view.file,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an SVG image from the ExcalidrawAutomate elements and the template provided.
|
||||
* @param {string} [templatePath] - The template path to use for the SVG.
|
||||
@@ -1848,7 +1914,7 @@ export class ExcalidrawAutomate {
|
||||
*/
|
||||
async addLaTex(topX: number, topY: number, tex: string): Promise<string> {
|
||||
const id = nanoid();
|
||||
const image = await tex2dataURL(tex, 4, this.plugin.app);
|
||||
const image = await tex2dataURL(tex, 4, this.plugin);
|
||||
if (!image) {
|
||||
return null;
|
||||
}
|
||||
@@ -1890,7 +1956,7 @@ export class ExcalidrawAutomate {
|
||||
created: number;
|
||||
size: { height: number; width: number };
|
||||
}> {
|
||||
return await tex2dataURL(tex,scale, this.plugin.app);
|
||||
return await tex2dataURL(tex,scale, this.plugin);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1462,7 +1462,7 @@ export class ExcalidrawData {
|
||||
: "";
|
||||
if (this.equations.size > 0) {
|
||||
for (const key of this.equations.keys()) {
|
||||
outString += `${key}: $$${this.equations.get(key).latex}$$\n\n`;
|
||||
outString += `${key}: $$${this.equations.get(key).latex.trim()}$$\n\n`;
|
||||
}
|
||||
}
|
||||
if (this.files.size > 0) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { DataURL } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import ExcalidrawView from "../view/ExcalidrawView";
|
||||
import { FileData, MimeType } from "./EmbeddedFileLoader";
|
||||
import { FileId } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { App } from "obsidian";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
|
||||
declare const loadMathjaxToSVG: Function;
|
||||
let mathjaxLoaded = false;
|
||||
@@ -52,7 +52,7 @@ export const updateEquation = async (
|
||||
export async function tex2dataURL(
|
||||
tex: string,
|
||||
scale: number = 4,
|
||||
app: App,
|
||||
plugin: ExcalidrawPlugin,
|
||||
): Promise<{
|
||||
mimeType: MimeType;
|
||||
fileId: FileId;
|
||||
@@ -61,7 +61,7 @@ export async function tex2dataURL(
|
||||
size: { height: number; width: number };
|
||||
}> {
|
||||
await loadMathJax();
|
||||
return tex2dataURLExternal(tex, scale, app);
|
||||
return tex2dataURLExternal(tex, scale, plugin);
|
||||
}
|
||||
|
||||
export const clearMathJaxVariables = () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ function createWorkerBlob(jsCode:string) {
|
||||
const workerCode = `
|
||||
var LZString=function(){var r=String.fromCharCode,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",e={};function t(r,o){if(!e[r]){e[r]={};for(var n=0;n<r.length;n++)e[r][r.charAt(n)]=n}return e[r][o]}var i={compressToBase64:function(r){if(null==r)return"";var n=i._compress(r,6,function(r){return o.charAt(r)});switch(n.length%4){default:case 0:return n;case 1:return n+"===";case 2:return n+"==";case 3:return n+"="}},decompressFromBase64:function(r){return null==r?"":""==r?null:i._decompress(r.length,32,function(n){return t(o,r.charAt(n))})},compressToUTF16:function(o){return null==o?"":i._compress(o,15,function(o){return r(o+32)})+" "},decompressFromUTF16:function(r){return null==r?"":""==r?null:i._decompress(r.length,16384,function(o){return r.charCodeAt(o)-32})},compressToUint8Array:function(r){for(var o=i.compress(r),n=new Uint8Array(2*o.length),e=0,t=o.length;e<t;e++){var s=o.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null==o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;e<t;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(r){return null==r?"":i._compress(r,6,function(r){return n.charAt(r)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(o){return t(n,r.charAt(o))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(r,o,n){if(null==r)return"";var e,t,i,s={},u={},a="",p="",c="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;i<r.length;i+=1)if(a=r.charAt(i),Object.prototype.hasOwnProperty.call(s,a)||(s[a]=f++,u[a]=!0),p=c+a,Object.prototype.hasOwnProperty.call(s,p))c=p;else{if(Object.prototype.hasOwnProperty.call(u,c)){if(c.charCodeAt(0)<256){for(e=0;e<h;e++)m<<=1,v==o-1?(v=0,d.push(n(m)),m=0):v++;for(t=c.charCodeAt(0),e=0;e<8;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;e<h;e++)m=m<<1|t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=c.charCodeAt(0),e=0;e<16;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}0==--l&&(l=Math.pow(2,h),h++),delete u[c]}else for(t=s[c],e=0;e<h;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;0==--l&&(l=Math.pow(2,h),h++),s[p]=f++,c=String(a)}if(""!==c){if(Object.prototype.hasOwnProperty.call(u,c)){if(c.charCodeAt(0)<256){for(e=0;e<h;e++)m<<=1,v==o-1?(v=0,d.push(n(m)),m=0):v++;for(t=c.charCodeAt(0),e=0;e<8;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;e<h;e++)m=m<<1|t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=c.charCodeAt(0),e=0;e<16;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}0==--l&&(l=Math.pow(2,h),h++),delete u[c]}else for(t=s[c],e=0;e<h;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;0==--l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;e<h;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==o-1){d.push(n(m));break}v++}return d.join("")},decompress:function(r){return null==r?"":""==r?null:i._decompress(r.length,32768,function(o){return r.charCodeAt(o)})},_decompress:function(o,n,e){var t,i,s,u,a,p,c,l=[],f=4,h=4,d=3,m="",v=[],g={val:e(0),position:n,index:1};for(t=0;t<3;t+=1)l[t]=t;for(s=0,a=Math.pow(2,2),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;switch(s){case 0:for(s=0,a=Math.pow(2,8),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;c=r(s);break;case 1:for(s=0,a=Math.pow(2,16),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;c=r(s);break;case 2:return""}for(l[3]=c,i=c,v.push(c);;){if(g.index>o)return"";for(s=0,a=Math.pow(2,d),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;switch(c=s){case 0:for(s=0,a=Math.pow(2,8),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;l[h++]=r(s),c=h-1,f--;break;case 1:for(s=0,a=Math.pow(2,16),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;l[h++]=r(s),c=h-1,f--;break;case 2:return v.join("")}if(0==f&&(f=Math.pow(2,d),d++),l[c])m=l[c];else{if(c!==h)return null;m=i+i.charAt(0)}v.push(m),l[h++]=i+m.charAt(0),i=m,0==--f&&(f=Math.pow(2,d),d++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module?module.exports=LZString:"undefined"!=typeof angular&&null!=angular&&angular.module("LZString",[]).factory("LZString",function(){return LZString});
|
||||
self.onmessage = function(e) {
|
||||
const { data, action } = e.data;
|
||||
const { data, action, messageId } = e.data;
|
||||
try {
|
||||
switch (action) {
|
||||
case 'compress':
|
||||
@@ -22,7 +22,7 @@ self.onmessage = function(e) {
|
||||
for (let i = 0; i < compressed.length; i += chunkSize) {
|
||||
result += compressed.slice(i, i + chunkSize) + '\\n\\n';
|
||||
}
|
||||
self.postMessage({ compressed: result.trim() });
|
||||
self.postMessage({ messageId, compressed: result.trim() });
|
||||
break;
|
||||
case 'decompress':
|
||||
if (!data) throw new Error("No input string provided for decompression.");
|
||||
@@ -35,14 +35,13 @@ self.onmessage = function(e) {
|
||||
}
|
||||
}
|
||||
const decompressed = LZString.decompressFromBase64(cleanedData);
|
||||
self.postMessage({ decompressed });
|
||||
self.postMessage({ messageId, decompressed });
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown action.");
|
||||
}
|
||||
} catch (error) {
|
||||
// Post the error message back to the main thread
|
||||
self.postMessage({ error: error.message });
|
||||
self.postMessage({ messageId, error: error.message });
|
||||
}
|
||||
};
|
||||
`;
|
||||
@@ -55,27 +54,48 @@ export function initCompressionWorker() {
|
||||
}
|
||||
}
|
||||
|
||||
export async function runCompressionWorker(data:string, action: 'compress' | 'decompress'): Promise<string> {
|
||||
let messageCounter = 0;
|
||||
const pendingOperations = new Map<number, {
|
||||
resolve: (value: string) => void,
|
||||
reject: (reason: Error) => void
|
||||
}>();
|
||||
|
||||
export async function runCompressionWorker(data: string, action: 'compress' | 'decompress'): Promise<string> {
|
||||
const messageId = messageCounter++;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
worker.onmessage = function(e) {
|
||||
const { compressed, decompressed, error } = e.data;
|
||||
|
||||
if (error) {
|
||||
reject(new Error(error));
|
||||
} else if (compressed || decompressed) {
|
||||
resolve(compressed || decompressed);
|
||||
} else {
|
||||
reject(new Error('Unexpected response from worker'));
|
||||
}
|
||||
};
|
||||
// Store the promise handlers
|
||||
pendingOperations.set(messageId, { resolve, reject });
|
||||
|
||||
// Set up the worker's error handler
|
||||
worker.onerror = function(error) {
|
||||
reject(new Error(error.message));
|
||||
};
|
||||
// Set up worker message handler if not already set
|
||||
if (!worker.onmessage) {
|
||||
worker.onmessage = function(e) {
|
||||
const { messageId, compressed, decompressed, error } = e.data;
|
||||
const handlers = pendingOperations.get(messageId);
|
||||
|
||||
if (handlers) {
|
||||
if (error) {
|
||||
handlers.reject(new Error(error));
|
||||
} else if (compressed || decompressed) {
|
||||
handlers.resolve(compressed || decompressed);
|
||||
} else {
|
||||
handlers.reject(new Error('Unexpected response from worker'));
|
||||
}
|
||||
pendingOperations.delete(messageId);
|
||||
}
|
||||
};
|
||||
|
||||
// Post the message to the worker
|
||||
worker.postMessage({ data, action });
|
||||
worker.onerror = function(error) {
|
||||
// Handle any pending operations in case of worker error
|
||||
for (const [id, handlers] of pendingOperations) {
|
||||
handlers.reject(new Error(error.message));
|
||||
pendingOperations.delete(id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Post message with ID
|
||||
worker.postMessage({ messageId, data, action });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,4 +17,11 @@ export enum PreviewImageType {
|
||||
PNG = "PNG",
|
||||
SVGIMG = "SVGIMG",
|
||||
SVG = "SVG"
|
||||
}
|
||||
|
||||
export interface FrameRenderingOptions {
|
||||
enabled: boolean;
|
||||
name: boolean;
|
||||
outline: boolean;
|
||||
clip: boolean;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NonDeletedExcalidrawElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { DEVICE, REG_LINKINDEX_INVALIDCHARS } from "src/constants/constants";
|
||||
import { getParentOfClass } from "./obsidianUtils";
|
||||
import { App, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import { App, Notice, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import { getLinkParts } from "./utils";
|
||||
import ExcalidrawView from "src/view/ExcalidrawView";
|
||||
|
||||
@@ -62,8 +62,12 @@ export function setFileToLocalGraph(app: App, file: TFile) {
|
||||
app.workspace.iterateAllLeaves((l) => {
|
||||
if (l.view?.getViewType() === "localgraph") lgv = l.view;
|
||||
});
|
||||
if (lgv) {
|
||||
//@ts-ignore
|
||||
lgv.loadFile(file);
|
||||
try {
|
||||
if (lgv) {
|
||||
//@ts-ignore
|
||||
lgv.loadFile(file);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,16 @@ import { t } from 'src/lang/helpers';
|
||||
|
||||
const DPI = 96;
|
||||
|
||||
export type PDFPageAlignment = "center" | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
|
||||
export type PDFPageAlignment =
|
||||
| "center"
|
||||
| "top-left"
|
||||
| "top-center"
|
||||
| "top-right"
|
||||
| "bottom-left"
|
||||
| "bottom-center"
|
||||
| "bottom-right"
|
||||
| "center-left"
|
||||
| "center-right";
|
||||
export type PDFPageMarginString = "none" | "tiny" | "normal";
|
||||
|
||||
export interface PDFExportScale {
|
||||
@@ -54,9 +63,9 @@ export type PageSize = keyof typeof STANDARD_PAGE_SIZES;
|
||||
export function getMarginValue(margin:PDFPageMarginString): PDFMargin {
|
||||
switch(margin) {
|
||||
case "none": return { left: 0, right: 0, top: 0, bottom: 0 };
|
||||
case "tiny": return { left: 5, right: 5, top: 5, bottom: 5 };
|
||||
case "normal": return { left: 25, right: 25, top: 25, bottom: 25 };
|
||||
default: return { left: 25, right: 25, top: 25, bottom: 25 };
|
||||
case "tiny": return { left: 10, right: 10, top: 10, bottom: 10 };
|
||||
case "normal": return { left: 60, right: 60, top: 60, bottom: 60 };
|
||||
default: return { left: 60, right: 60, top: 60, bottom: 60 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +212,7 @@ async function printPdf(
|
||||
}
|
||||
|
||||
function calculateDimensions(
|
||||
svg: SVGSVGElement,
|
||||
svgWidth: number,
|
||||
svgHeight: number,
|
||||
pageDim: PageDimensions,
|
||||
@@ -219,6 +229,9 @@ function calculateDimensions(
|
||||
}[],
|
||||
pages: number
|
||||
} {
|
||||
const viewBox = svg.getAttribute('viewBox')?.split(' ').map(Number) || [0, 0, svgWidth, svgHeight];
|
||||
const [viewBoxX, viewBoxY] = viewBox;
|
||||
|
||||
const availableWidth = pageDim.width - margin.left - margin.right;
|
||||
const availableHeight = pageDim.height - margin.top - margin.bottom;
|
||||
|
||||
@@ -262,7 +275,7 @@ function calculateDimensions(
|
||||
|
||||
return {
|
||||
tiles: [{
|
||||
viewBox: `0 0 ${svgWidth} ${svgHeight}`,
|
||||
viewBox: `${viewBoxX} ${viewBoxY} ${svgWidth} ${svgHeight}`,
|
||||
width: finalWidth,
|
||||
height: finalHeight,
|
||||
x: position.x,
|
||||
@@ -273,35 +286,98 @@ function calculateDimensions(
|
||||
}
|
||||
|
||||
// Content needs to be tiled
|
||||
const tiles = [];
|
||||
const cols = Math.ceil(finalWidth / availableWidth);
|
||||
const rows = Math.ceil(finalHeight / availableHeight);
|
||||
|
||||
// Calculate total available space considering all margins
|
||||
const totalAvailableWidth = cols * availableWidth;
|
||||
const totalAvailableHeight = rows * availableHeight;
|
||||
|
||||
// Calculate global position in the total available space
|
||||
const globalPosition = calculatePosition(
|
||||
finalWidth,
|
||||
finalHeight,
|
||||
totalAvailableWidth,
|
||||
totalAvailableHeight,
|
||||
{
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0
|
||||
},
|
||||
alignment
|
||||
);
|
||||
|
||||
const tiles = [];
|
||||
for (let row = 0; row < rows; row++) {
|
||||
for (let col = 0; col < cols; col++) {
|
||||
const tileX = (col * availableWidth) / (scale.zoom || 1);
|
||||
const tileY = (row * availableHeight) / (scale.zoom || 1);
|
||||
const tileWidth = Math.min(svgWidth - tileX, availableWidth / (scale.zoom || 1));
|
||||
const tileHeight = Math.min(svgHeight - tileY, availableHeight / (scale.zoom || 1));
|
||||
const scaledTileWidth = tileWidth * (scale.zoom || 1);
|
||||
const scaledTileHeight = tileHeight * (scale.zoom || 1);
|
||||
// Calculate where this tile intersects with the image in global space
|
||||
const tileGlobalX = col * availableWidth;
|
||||
const tileGlobalY = row * availableHeight;
|
||||
|
||||
// Calculate the intersection of the tile with the image
|
||||
const intersectX = Math.max(tileGlobalX, globalPosition.x);
|
||||
const intersectY = Math.max(tileGlobalY, globalPosition.y);
|
||||
const intersectRight = Math.min(tileGlobalX + availableWidth, globalPosition.x + finalWidth);
|
||||
const intersectBottom = Math.min(tileGlobalY + availableHeight, globalPosition.y + finalHeight);
|
||||
|
||||
// Calculate position for each tile
|
||||
const position = calculatePosition(
|
||||
scaledTileWidth,
|
||||
scaledTileHeight,
|
||||
pageDim.width,
|
||||
pageDim.height,
|
||||
margin,
|
||||
alignment
|
||||
);
|
||||
// Calculate actual tile dimensions
|
||||
const scaledTileWidth = Math.max(0, intersectRight - intersectX);
|
||||
const scaledTileHeight = Math.max(0, intersectBottom - intersectY);
|
||||
|
||||
if (scaledTileWidth <= 0 || scaledTileHeight <= 0) continue;
|
||||
|
||||
// Calculate viewBox coordinates
|
||||
const tileX = (intersectX - globalPosition.x) / (scale.zoom || 1);
|
||||
const tileY = (intersectY - globalPosition.y) / (scale.zoom || 1);
|
||||
const tileWidth = scaledTileWidth / (scale.zoom || 1);
|
||||
const tileHeight = scaledTileHeight / (scale.zoom || 1);
|
||||
|
||||
let x = margin.left;
|
||||
let y = margin.top;
|
||||
|
||||
// Handle horizontal positioning
|
||||
const widthRatio = scaledTileWidth / availableWidth;
|
||||
if (widthRatio >= 0.99 && widthRatio <= 1.01) {
|
||||
x = margin.left;
|
||||
} else {
|
||||
if (alignment === "center" || alignment === "top-center" || alignment === "bottom-center") {
|
||||
if(col === 0) {
|
||||
x = margin.left + (availableWidth - scaledTileWidth);
|
||||
} else {
|
||||
x = margin.left;
|
||||
}
|
||||
} else if (alignment.endsWith('right')) {
|
||||
x = pageDim.width - margin.right - scaledTileWidth;
|
||||
} else {
|
||||
x = margin.left;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle vertical positioning
|
||||
const heightRatio = scaledTileHeight / availableHeight;
|
||||
if (heightRatio >= 0.99 && heightRatio <= 1.01) {
|
||||
y = margin.top;
|
||||
} else {
|
||||
if (alignment === "center" || alignment === "center-left" || alignment === "center-right") {
|
||||
if(row === 0) {
|
||||
y = margin.top + (availableHeight - scaledTileHeight);
|
||||
} else {
|
||||
y = margin.top;
|
||||
}
|
||||
} else if (alignment.startsWith('bottom')) {
|
||||
y = pageDim.height - margin.bottom - scaledTileHeight;
|
||||
} else {
|
||||
y = margin.top;
|
||||
}
|
||||
}
|
||||
|
||||
tiles.push({
|
||||
viewBox: `${tileX} ${tileY} ${tileWidth} ${tileHeight}`,
|
||||
viewBox: `${tileX + viewBoxX} ${tileY + viewBoxY} ${tileWidth} ${tileHeight}`,
|
||||
width: scaledTileWidth,
|
||||
height: scaledTileHeight,
|
||||
x: position.x,
|
||||
y: position.y
|
||||
x: x,
|
||||
y: y
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -324,14 +400,14 @@ function calculatePosition(
|
||||
let y = margin.top;
|
||||
|
||||
// Handle horizontal alignment
|
||||
if (alignment.includes('center')) {
|
||||
if (alignment === "center" || alignment === "top-center" || alignment === "bottom-center") {
|
||||
x = margin.left + (availableWidth - svgWidth) / 2;
|
||||
} else if (alignment.includes('right')) {
|
||||
} else if (alignment.endsWith('right')) {
|
||||
x = margin.left + availableWidth - svgWidth;
|
||||
}
|
||||
|
||||
// Handle vertical alignment
|
||||
if (alignment.startsWith('center')) {
|
||||
if (alignment === "center" || alignment === "center-left" || alignment === "center-right") {
|
||||
y = margin.top + (availableHeight - svgHeight) / 2;
|
||||
} else if (alignment.startsWith('bottom')) {
|
||||
y = margin.top + availableHeight - svgHeight;
|
||||
@@ -364,12 +440,13 @@ export async function exportToPDF({
|
||||
allPagesDiv.style.width = "100%";
|
||||
allPagesDiv.style.height = "fit-content";
|
||||
|
||||
let j = 1;
|
||||
let j = 0;
|
||||
for (const svg of SVG) {
|
||||
const svgWidth = parseFloat(svg.getAttribute('width') || '0');
|
||||
const svgHeight = parseFloat(svg.getAttribute('height') || '0');
|
||||
|
||||
const {tiles} = calculateDimensions(
|
||||
svg,
|
||||
svgWidth,
|
||||
svgHeight,
|
||||
pageProps.dimensions,
|
||||
@@ -378,7 +455,7 @@ export async function exportToPDF({
|
||||
pageProps.alignment
|
||||
);
|
||||
|
||||
let i = 1;
|
||||
let i = 0;
|
||||
for (const tile of tiles) {
|
||||
const pageDiv = createDiv();
|
||||
pageDiv.style.width = `${width}px`;
|
||||
@@ -394,7 +471,7 @@ export async function exportToPDF({
|
||||
clonedSVG.style.height = `${tile.height}px`;
|
||||
clonedSVG.style.position = 'absolute';
|
||||
clonedSVG.style.left = `${tile.x}px`;
|
||||
clonedSVG.style.top = `${tile.y + (i-1)*height}px`;
|
||||
clonedSVG.style.top = `${tile.y + (i+j)*height}px`;
|
||||
|
||||
pageDiv.appendChild(clonedSVG);
|
||||
allPagesDiv.appendChild(pageDiv);
|
||||
|
||||
@@ -307,7 +307,7 @@ export async function getSVG (
|
||||
: {},
|
||||
},
|
||||
files: scene.files,
|
||||
exportPadding: exportSettings.frameRendering ? 0 : padding,
|
||||
exportPadding: exportSettings.frameRendering?.enabled ? 0 : padding,
|
||||
exportingFrame: null,
|
||||
renderEmbeddables: true,
|
||||
skipInliningFonts: exportSettings.skipInliningFonts,
|
||||
@@ -365,7 +365,7 @@ export async function getPNG (
|
||||
: {},
|
||||
},
|
||||
files: filterFiles(scene.files),
|
||||
exportPadding: exportSettings.frameRendering ? 0 : padding,
|
||||
exportPadding: exportSettings.frameRendering?.enabled ? 0 : padding,
|
||||
mimeType: "image/png",
|
||||
getDimensions: (width: number, height: number) => ({
|
||||
width: width * scale,
|
||||
|
||||
@@ -153,6 +153,7 @@ import { DropManager } from "./managers/DropManager";
|
||||
import { ImageInfo } from "src/types/excalidrawAutomateTypes";
|
||||
import { exportToPDF, getMarginValue, getPageDimensions, PageOrientation, PageSize } from "src/utils/exportUtils";
|
||||
import { create } from "domain";
|
||||
import { FrameRenderingOptions } from "src/types/utilTypes";
|
||||
|
||||
const EMBEDDABLE_SEMAPHORE_TIMEOUT = 2000;
|
||||
const PREVENT_RELOAD_TIMEOUT = 2000;
|
||||
@@ -186,12 +187,7 @@ export interface ExportSettings {
|
||||
withBackground: boolean;
|
||||
withTheme: boolean;
|
||||
isMask: boolean;
|
||||
frameRendering?: { //optional, overrides relevant appState settings for rendering the frame
|
||||
enabled: boolean;
|
||||
name: boolean;
|
||||
outline: boolean;
|
||||
clip: boolean;
|
||||
};
|
||||
frameRendering?: FrameRenderingOptions; //optional, overrides relevant appState settings for rendering the frame
|
||||
skipInliningFonts?: boolean;
|
||||
}
|
||||
|
||||
@@ -475,36 +471,75 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
);
|
||||
}
|
||||
|
||||
public getViewExportTheme(theme?:string):string {
|
||||
if(theme) return theme;
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
}
|
||||
const ed = this.exportDialog;
|
||||
return ed ? ed.theme : getExportTheme(this.plugin, this.file, this.excalidrawAPI.getAppState().theme)
|
||||
}
|
||||
|
||||
public getViewExportEmbedScene(embedScene?:boolean):boolean {
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
}
|
||||
const ed = this.exportDialog;
|
||||
return typeof embedScene === "undefined"
|
||||
? (ed ? ed.embedScene : false)
|
||||
: embedScene;
|
||||
}
|
||||
|
||||
public getViewExportPadding(padding?: number): number {
|
||||
if(typeof padding !== "undefined") return padding;
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
}
|
||||
const ed = this.exportDialog;
|
||||
return ed ? ed.padding : getExportPadding(this.plugin, this.file)
|
||||
}
|
||||
|
||||
public getViewExportScale(scale?: number): number {
|
||||
if(typeof scale !== "undefined") return scale;
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
}
|
||||
const ed = this.exportDialog;
|
||||
return ed ? ed.scale : getPNGScale(this.plugin, this.file);
|
||||
}
|
||||
|
||||
public getViewExportWithBackground(withBackground?:boolean) {
|
||||
if(typeof withBackground !== "undefined") return withBackground;
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
}
|
||||
const ed = this.exportDialog;
|
||||
return ed ? !ed.transparent : getWithBackground(this.plugin, this.file)
|
||||
}
|
||||
|
||||
public async svg(scene: any, theme?:string, embedScene?: boolean, embedFont: boolean = false): Promise<SVGSVGElement> {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.svg, "ExcalidrawView.svg", scene, theme, embedScene);
|
||||
const ed = this.exportDialog;
|
||||
|
||||
const exportSettings: ExportSettings = {
|
||||
withBackground: ed ? !ed.transparent : getWithBackground(this.plugin, this.file),
|
||||
withBackground: this.getViewExportWithBackground(),
|
||||
withTheme: true,
|
||||
isMask: isMaskFile(this.plugin, this.file),
|
||||
skipInliningFonts: !embedFont,
|
||||
};
|
||||
|
||||
if(typeof embedScene === "undefined") {
|
||||
embedScene = shouldEmbedScene(this.plugin, this.file);
|
||||
}
|
||||
|
||||
return await getSVG(
|
||||
{
|
||||
...scene,
|
||||
...{
|
||||
appState: {
|
||||
...scene.appState,
|
||||
theme: theme ?? (ed ? ed.theme : getExportTheme(this.plugin, this.file, scene.appState.theme)),
|
||||
exportEmbedScene: typeof embedScene === "undefined"
|
||||
? (ed ? ed.embedScene : false)
|
||||
: embedScene,
|
||||
theme: this.getViewExportTheme(theme),
|
||||
exportEmbedScene: this.getViewExportEmbedScene(embedScene),
|
||||
},
|
||||
},
|
||||
},
|
||||
exportSettings,
|
||||
ed ? ed.padding : getExportPadding(this.plugin, this.file),
|
||||
this.getViewExportPadding(),
|
||||
this.file,
|
||||
);
|
||||
}
|
||||
@@ -567,7 +602,6 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
}
|
||||
|
||||
public async exportPDF(
|
||||
toVault: boolean,
|
||||
selectedOnly?: boolean,
|
||||
pageSize: PageSize = "A4",
|
||||
orientation: PageOrientation = "portrait"
|
||||
@@ -605,34 +639,27 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
|
||||
public async png(scene: any, theme?:string, embedScene?: boolean): Promise<Blob> {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.png, "ExcalidrawView.png", scene, theme, embedScene);
|
||||
const ed = this.exportDialog;
|
||||
|
||||
const exportSettings: ExportSettings = {
|
||||
withBackground: ed ? !ed.transparent : getWithBackground(this.plugin, this.file),
|
||||
withBackground: this.getViewExportWithBackground(),
|
||||
withTheme: true,
|
||||
isMask: isMaskFile(this.plugin, this.file),
|
||||
};
|
||||
|
||||
if(typeof embedScene === "undefined") {
|
||||
embedScene = shouldEmbedScene(this.plugin, this.file);
|
||||
}
|
||||
|
||||
return await getPNG(
|
||||
{
|
||||
...scene,
|
||||
...{
|
||||
appState: {
|
||||
...scene.appState,
|
||||
theme: theme ?? (ed ? ed.theme : getExportTheme(this.plugin, this.file, scene.appState.theme)),
|
||||
exportEmbedScene: typeof embedScene === "undefined"
|
||||
? (ed ? ed.embedScene : false)
|
||||
: embedScene,
|
||||
theme: this.getViewExportTheme(theme),
|
||||
exportEmbedScene: this.getViewExportEmbedScene(embedScene),
|
||||
},
|
||||
},
|
||||
},
|
||||
exportSettings,
|
||||
ed ? ed.padding : getExportPadding(this.plugin, this.file),
|
||||
ed ? ed.scale : getPNGScale(this.plugin, this.file),
|
||||
this.getViewExportPadding(),
|
||||
this.getViewExportScale(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3095,7 +3122,6 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
}
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
this.exportDialog.createForm();
|
||||
}
|
||||
this.exportDialog.open();
|
||||
})
|
||||
@@ -4892,7 +4918,6 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
private actionOpenExportImageDialog() {
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
this.exportDialog.createForm();
|
||||
}
|
||||
this.exportDialog.open();
|
||||
}
|
||||
|
||||
30
styles.css
30
styles.css
@@ -342,7 +342,9 @@ label.color-input-container > input {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.excalidraw .panelColumn {
|
||||
.excalidraw .selected-shape-actions .panelColumn,
|
||||
.excalidraw .App-mobile-menu .panelColumn
|
||||
{
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
@@ -388,11 +390,14 @@ div.excalidraw-draginfo {
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
/*Arrow Picker Popover Overflow*/
|
||||
/*
|
||||
.excalidraw .selected-shape-actions .Island.App-menu__left,
|
||||
.excalidraw .selected-shape-actions .Island.App-menu__left .panelColumn,
|
||||
.excalidraw .Island .App-mobile-menu,
|
||||
.excalidraw .Island.App-menu__left {
|
||||
/*Arrow Picker Popover Overflow*/
|
||||
.excalidraw .Island .App-mobile-menu .panelColumn {
|
||||
overflow: visible;
|
||||
}
|
||||
}*/
|
||||
|
||||
.excalidraw__embeddable-container .view-header {
|
||||
display: none !important;
|
||||
@@ -712,4 +717,21 @@ textarea.excalidraw-wysiwyg, .excalidraw input {
|
||||
.excalidraw-release .nav-button.is-active {
|
||||
border-bottom: 2px solid var(--interactive-accent);
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.excalidraw .picker-content {
|
||||
grid-gap: 0.2rem !important;
|
||||
}
|
||||
|
||||
.excalidraw .picker-content button.picker-option{
|
||||
width: 1.85rem !important;
|
||||
height: 1.85rem !important;
|
||||
}
|
||||
|
||||
.excalidraw .picker {
|
||||
padding: 0.2rem !important;
|
||||
}
|
||||
|
||||
.excalidraw .context-menu {
|
||||
height: fit-content;
|
||||
}
|
||||
Reference in New Issue
Block a user