PDF export: HD Screen and Martch Image + disable double click text create
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled

This commit is contained in:
zsviczian
2025-04-26 08:13:06 +02:00
parent a48222022e
commit 4a803f4b46
9 changed files with 92 additions and 39 deletions

View File

@@ -23,7 +23,7 @@
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.11.8",
"@zsviczian/excalidraw": "0.18.0-8",
"@zsviczian/excalidraw": "0.18.0-9",
"chroma-js": "^2.4.2",
"clsx": "^2.0.0",
"@zsviczian/colormaster": "^1.2.2",

View File

@@ -46,6 +46,7 @@ import { PDFExportSettingsComponent, PDFExportSettings } from "src/shared/Dialog
import de from "src/lang/locale/de";
export interface ExcalidrawSettings {
disableDoubleClickTextEditing: boolean;
folder: string;
cropFolder: string;
annotateFolder: string;
@@ -228,6 +229,7 @@ export interface ExcalidrawSettings {
declare const PLUGIN_VERSION:string;
export const DEFAULT_SETTINGS: ExcalidrawSettings = {
disableDoubleClickTextEditing: false,
folder: "Excalidraw",
cropFolder: "",
annotateFolder: "",
@@ -684,6 +686,17 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
new Setting(detailsEl)
.setName(t("TOGGLE_SPLASHSCREEN"))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.showSplashscreen)
.onChange((value)=> {
this.plugin.settings.showSplashscreen = value;
this.applySettingsUpdate();
})
)
new Setting(detailsEl)
.setName(t("FOLDER_NAME"))
.setDesc(fragWithHTML(t("FOLDER_DESC")))
@@ -1094,6 +1107,17 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
cls: "excalidraw-setting-h1",
});
new Setting(detailsEl)
.setName(t("ENABLE_DOUBLE_CLICK_TEXT_EDITING_NAME"))
.addToggle((toggle) =>
toggle
.setValue(!this.plugin.settings.disableDoubleClickTextEditing)
.onChange(async (value) => {
this.plugin.settings.disableDoubleClickTextEditing = !value;
this.applySettingsUpdate();
}),
);
const readingModeEl = new Setting(detailsEl)
.setName(t("SHOW_DRAWING_OR_MD_IN_READING_MODE_NAME"))
.setDesc(fragWithHTML(t("SHOW_DRAWING_OR_MD_IN_READING_MODE_DESC")))
@@ -1136,17 +1160,6 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
);
addIframe(detailsEl, "H8Njp7ZXYag",999);
new Setting(detailsEl)
.setName(t("TOGGLE_SPLASHSCREEN"))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.showSplashscreen)
.onChange((value)=> {
this.plugin.settings.showSplashscreen = value;
this.applySettingsUpdate();
})
)
detailsEl = displayDetailsEl.createEl("details");
detailsEl.createEl("summary", {
text: t("HOTKEY_OVERRIDE_HEAD"),

View File

@@ -371,6 +371,7 @@ FILENAME_HEAD: "Filename",
DEFAULT_PEN_MODE_NAME: "Pen mode",
DEFAULT_PEN_MODE_DESC:
"Should pen mode be automatically enabled when opening Excalidraw?",
ENABLE_DOUBLE_CLICK_TEXT_EDITING_NAME: "Enable double-click text create",
DISABLE_DOUBLE_TAP_ERASER_NAME: "Enable double-tap eraser in pen mode",
DISABLE_SINGLE_FINGER_PANNING_NAME: "Enable single-finger panning in pen mode",
SHOW_PEN_MODE_FREEDRAW_CROSSHAIR_NAME: "Show (+) crosshair in pen mode",

View File

@@ -43,6 +43,7 @@ export class ExportDialog extends Modal {
public customPaperColor: string = "#ffffff";
public alignment: PDFPageAlignment = "center";
public margin: PDFPageMarginString = "normal";
private scaleSetting:Setting;
constructor(
private plugin: ExcalidrawPlugin,
@@ -85,6 +86,17 @@ export class ExportDialog extends Modal {
this.selectedOnlySetting = null;
this.containerEl.remove();
}
updateBoundingBox() {
if(this.hasSelectedElements && this.exportSelectedOnly) {
this.boundingBox = this.ea.getBoundingBox(this.view.getViewSelectedElements());
} else {
this.boundingBox = this.ea.getBoundingBox(this.ea.getViewElements());
}
if(this.scaleSetting) {
this.scaleSetting.setDesc(this.size());
}
}
onOpen(): void {
this.containerEl.classList.add("excalidraw-release");
@@ -92,6 +104,7 @@ export class ExportDialog extends Modal {
this.hasSelectedElements = this.view.getViewSelectedElements().length > 0;
//@ts-ignore
this.selectedOnlySetting.setVisibility(this.hasSelectedElements);
this.updateBoundingBox();
}
async onClose() {
@@ -167,9 +180,14 @@ export class ExportDialog extends Modal {
this.createPDFButton();
}
}
private size ():DocumentFragment {
const width = Math.round(this.scale*this.boundingBox.width + this.padding*2);
const height = Math.round(this.scale*this.boundingBox.height + this.padding*2);
return fragWithHTML(`${t("EXPORTDIALOG_SIZE_DESC")}<br>${t("EXPORTDIALOG_SCALE_VALUE")} <b>${this.scale}</b><br>${t("EXPORTDIALOG_IMAGE_SIZE")} <b>${width}x${height}</b>`);
}
private createImageSettings() {
let scaleSetting:Setting;
let paddingSetting: Setting;
this.contentContainer.createEl("h1",{text: t("EXPORTDIALOG_IMAGE_SETTINGS")});
@@ -177,12 +195,6 @@ export class ExportDialog extends Modal {
this.createSaveSettingsDropdown();
const size = ():DocumentFragment => {
const width = Math.round(this.scale*this.boundingBox.width + this.padding*2);
const height = Math.round(this.scale*this.boundingBox.height + this.padding*2);
return fragWithHTML(`${t("EXPORTDIALOG_SIZE_DESC")}<br>${t("EXPORTDIALOG_SCALE_VALUE")} <b>${this.scale}</b><br>${t("EXPORTDIALOG_IMAGE_SIZE")} <b>${width}x${height}</b>`);
}
const padding = ():DocumentFragment => {
return fragWithHTML(`${t("EXPORTDIALOG_CURRENT_PADDING")} <b>${this.padding}</b>`);
}
@@ -196,21 +208,21 @@ export class ExportDialog extends Modal {
.setValue(this.padding)
.onChange(value => {
this.padding = value;
scaleSetting.setDesc(size());
this.scaleSetting.setDesc(this.size());
paddingSetting.setDesc(padding());
})
})
scaleSetting = new Setting(this.contentContainer)
this.scaleSetting = new Setting(this.contentContainer)
.setName(t("EXPORTDIALOG_SCALE"))
.setDesc(size())
.setDesc(this.size())
.addSlider(slider =>
slider
.setLimits(0.2,7,0.1)
.setValue(this.scale)
.onChange(value => {
this.scale = value;
scaleSetting.setDesc(size());
this.scaleSetting.setDesc(this.size());
})
)
@@ -247,6 +259,7 @@ export class ExportDialog extends Modal {
.setValue(this.exportSelectedOnly?"selected":"all")
.onChange(value => {
this.exportSelectedOnly = value === "selected";
this.updateBoundingBox();
})
);
}

View File

@@ -70,7 +70,7 @@ export class InsertPDFModal extends Modal {
this.setImageSizeMessage = null;
}
private async getPageDimensions (pdfDoc: any) {
private async getPDFPageDimensions (pdfDoc: any) {
try {
const scale = this.plugin.settings.pdfScale;
const canvas = createEl("canvas");
@@ -197,7 +197,7 @@ export class InsertPDFModal extends Modal {
rangeOnChange(`1-${numPages}`);
importButtonMessages();
numPagesMessages();
this.getPageDimensions(this.pdfDoc);
this.getPDFPageDimensions(this.pdfDoc);
} else {
importButton.setDisabled(true);
}

View File

@@ -23,6 +23,8 @@ I build this plugin in my free time, as a labor of love. Curious about the philo
## New
- Expose parameter in plugin settings to disable AI functionality [#2325](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2325)
- Enable double-click text editing option in Excalidraw appearance and behavior (based on request on Discord)
- Added two new PDF export sizes: "Match image", "HD Screen".
## Fixed in the plugin
- Scaling multiple embeddables at once did not work. [#2276](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2276)

View File

@@ -21,6 +21,10 @@ export class PDFExportSettingsComponent {
if (!update) this.update = () => {};
}
isOrientationAndTilingVisible() {
return !(this.settings.pageSize === "HD Screen" || this.settings.pageSize === "MATCH IMAGE");
}
render() {
const pageSizeOptions: Record<string, string> = Object.keys(STANDARD_PAGE_SIZES)
.reduce((acc, key) => ({
@@ -28,6 +32,8 @@ export class PDFExportSettingsComponent {
[key]: key
}), {});
let div: HTMLDivElement;
new Setting(this.contentEl)
.setName(t("EXPORTDIALOG_PAGE_SIZE"))
.addDropdown(dropdown =>
@@ -36,11 +42,15 @@ export class PDFExportSettingsComponent {
.setValue(this.settings.pageSize)
.onChange(value => {
this.settings.pageSize = value as PageSize;
div.style.display = this.isOrientationAndTilingVisible() ? "block" : "none";
this.update();
})
);
new Setting(this.contentEl)
div = this.contentEl.createDiv();
div.style.display = this.isOrientationAndTilingVisible() ? "block" : "none";
new Setting(div)
.setName(t("EXPORTDIALOG_PAGE_ORIENTATION"))
.addDropdown(dropdown =>
dropdown
@@ -55,7 +65,7 @@ export class PDFExportSettingsComponent {
})
);
new Setting(this.contentEl)
new Setting(div)
.setName(t("EXPORTDIALOG_PDF_FIT_TO_PAGE"))
.addDropdown(dropdown =>
dropdown

View File

@@ -54,7 +54,9 @@ export const STANDARD_PAGE_SIZES = {
Legal: { width: 816, height: 1344 }, // 8.5 × 14 inches
Letter: { width: 816, height: 1056 }, // 8.5 × 11 inches
Tabloid: { width: 1056, height: 1632 }, // 11 × 17 inches
Ledger: { width: 1632, height: 1056 } // 17 × 11 inches
Ledger: { width: 1632, height: 1056 }, // 17 × 11 inches
"HD Screen": { width: 1920, height: 1080 },// 16:9 aspect ratio
"MATCH IMAGE": { width: 0, height: 0 }, // 0 means use the current screen size
} as const;
export type PageSize = keyof typeof STANDARD_PAGE_SIZES;
@@ -69,9 +71,15 @@ export function getMarginValue(margin:PDFPageMarginString): PDFMargin {
}
}
export function getPageDimensions(pageSize: PageSize, orientation: PageOrientation): PageDimensions {
const dimensions = STANDARD_PAGE_SIZES[pageSize];
return orientation === "portrait"
export function getPageDimensions(pageSize: PageSize, orientation: PageOrientation, dims?: {width: number, height: number}): PageDimensions {
let dimensions:{width: number, height: number};
dimensions = STANDARD_PAGE_SIZES[pageSize];
if (dims && dimensions.width === 0 && dimensions.height === 0) {
dimensions = { width: dims.width, height: dims.height };
}
return orientation === "portrait" || pageSize === "MATCH IMAGE" || pageSize === "HD Screen"
? { width: dimensions.width, height: dimensions.height }
: { width: dimensions.height, height: dimensions.width };
}

View File

@@ -77,7 +77,6 @@ import {
getExcalidrawMarkdownHeaderSection,
} from "../shared/ExcalidrawData";
import {
arrayBufferToBase64,
checkAndCreateFolder,
createOrOverwriteFile,
download,
@@ -155,7 +154,6 @@ import { ImageInfo } from "src/types/excalidrawAutomateTypes";
import { exportToPDF, getMarginValue, getPageDimensions, PageOrientation, PageSize } from "src/utils/exportUtils";
import { FrameRenderingOptions } from "src/types/utilTypes";
import { CaptureUpdateAction } from "src/constants/constants";
import { FlipHorizontal } from "lucide-react";
const EMBEDDABLE_SEMAPHORE_TIMEOUT = 2000;
const PREVENT_RELOAD_TIMEOUT = 2000;
@@ -612,8 +610,10 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
return;
}
const scene = this.getScene(selectedOnly);
const svg = await this.svg(
this.getScene(selectedOnly),
scene,
undefined,
false,
true
@@ -622,20 +622,26 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
if (!svg) {
return;
}
const boundingBox = this.plugin.ea.getBoundingBox(scene.elements);
const margin = getMarginValue(this.exportDialog.margin);
const [width, height] = [boundingBox.width, boundingBox.height];
exportToPDF({
SVG: [svg],
scale: {
zoom: this.exportDialog.scale,
fitToPage: this.exportDialog.fitToPage
fitToPage: pageSize === "MATCH IMAGE" || pageSize === "HD Screen"
? 1
: this.exportDialog.fitToPage
},
pageProps: {
dimensions: getPageDimensions(pageSize, orientation),
dimensions: getPageDimensions(pageSize, orientation, {width, height}),
backgroundColor: this.exportDialog.getPaperColor(),
margin: getMarginValue(this.exportDialog.margin),
margin,
alignment: this.exportDialog.alignment,
},
filename: this.file.basename,
filename: this.file.basename+".pdf",
});
}