mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 064e17b29d | |||
| 0aaba80c82 |
Generated
+8
-115
@@ -9,7 +9,6 @@
|
||||
"version": "2.2.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cantoo/pdf-lib": "^2.2.4",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zsviczian/colormaster": "^1.2.2",
|
||||
"@zsviczian/excalidraw": "0.17.6-27",
|
||||
@@ -1820,9 +1819,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz",
|
||||
"integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==",
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
@@ -1884,19 +1883,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz",
|
||||
"integrity": "sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg=="
|
||||
},
|
||||
"node_modules/@cantoo/pdf-lib": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@cantoo/pdf-lib/-/pdf-lib-2.2.4.tgz",
|
||||
"integrity": "sha512-69bECXjl0QVMrBTEsPZDPFmhoyLGwXKiJI5JWPgBnAx0kS5+7v76rNCeYMTbDmZaPgvdIViMvTaz2HVCe8u1fw==",
|
||||
"dependencies": {
|
||||
"@pdf-lib/standard-fonts": "^1.0.0",
|
||||
"@pdf-lib/upng": "^1.0.1",
|
||||
"color": "^4.2.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"node-html-better-parser": "^1.4.0",
|
||||
"pako": "^1.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz",
|
||||
@@ -2361,22 +2347,6 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@pdf-lib/standard-fonts": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
|
||||
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
|
||||
"dependencies": {
|
||||
"pako": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@pdf-lib/upng": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
|
||||
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
|
||||
"dependencies": {
|
||||
"pako": "^1.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
@@ -3477,9 +3447,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
|
||||
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@@ -3848,18 +3818,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1",
|
||||
"color-string": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
@@ -3872,32 +3830,8 @@
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||
},
|
||||
"node_modules/color-string": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||
"dependencies": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/color/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/colord": {
|
||||
"version": "2.9.3",
|
||||
@@ -4010,11 +3944,6 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
|
||||
},
|
||||
"node_modules/css-declaration-sorter": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz",
|
||||
@@ -5594,21 +5523,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/html-entities": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz",
|
||||
"integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/mdevils"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://patreon.com/mdevils"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
@@ -5703,11 +5617,6 @@
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
@@ -6830,14 +6739,6 @@
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/node-html-better-parser": {
|
||||
"version": "1.4.7",
|
||||
"resolved": "https://registry.npmjs.org/node-html-better-parser/-/node-html-better-parser-1.4.7.tgz",
|
||||
"integrity": "sha512-cJcBhlrn432XtbzPmzxxMLsHeSUnE5qFQtVbVlHXvUGt2ccqc0/wBsB43CFv39BIkLkP5rrPUN5Hg52CnheH+A==",
|
||||
"dependencies": {
|
||||
"html-entities": "^2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
@@ -8410,14 +8311,6 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
|
||||
+1
-2
@@ -40,8 +40,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"roughjs": "^4.5.2",
|
||||
"woff2sfnt-sfnt2woff": "^1.0.0",
|
||||
"es6-promise-pool": "2.5.0",
|
||||
"@cantoo/pdf-lib": "^2.2.4"
|
||||
"es6-promise-pool": "2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jsesc": "^3.0.2",
|
||||
|
||||
@@ -507,7 +507,6 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
customPaperColor: "#ffffff",
|
||||
alignment: "center",
|
||||
margin: "normal",
|
||||
exportDPI: 300,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1048,7 +1048,6 @@ FILENAME_HEAD: "Filename",
|
||||
EXPORTDIALOG_PAGE_ORIENTATION: "Orientation",
|
||||
EXPORTDIALOG_ORIENTATION_PORTRAIT: "Portrait",
|
||||
EXPORTDIALOG_ORIENTATION_LANDSCAPE: "Landscape",
|
||||
EXPORTDIALOG_PDF_DPI: "Image quality [DPI]",
|
||||
EXPORTDIALOG_PDF_FIT_TO_PAGE: "Page Fitting",
|
||||
EXPORTDIALOG_PDF_FIT_OPTION: "Fit to page",
|
||||
EXPORTDIALOG_PDF_FIT_2_OPTION: "Fit to 2-pages",
|
||||
@@ -1085,9 +1084,11 @@ FILENAME_HEAD: "Filename",
|
||||
EXPORTDIALOG_PNGTOCLIPBOARD : "PNG to Clipboard",
|
||||
EXPORTDIALOG_SVGTOCLIPBOARD : "SVG to Clipboard",
|
||||
EXPORTDIALOG_PDF: "Export PDF",
|
||||
EXPORTDIALOG_PDFTOVAULT: "PDF to Vault",
|
||||
|
||||
EXPORTDIALOG_PDF_PROGRESS_NOTICE: "Exporting page",
|
||||
EXPORTDIALOG_PDF_PROGRESS_IMAGE: "of image",
|
||||
EXPORTDIALOG_PDF_PROGRESS_NOTICE: "Exporting PDF. If this image is large, it may take a while.",
|
||||
EXPORTDIALOG_PDF_PROGRESS_DONE: "Export complete",
|
||||
EXPORTDIALOG_PDF_PROGRESS_ERROR: "Error exporting PDF, check developer console for details",
|
||||
|
||||
//exportUtils.ts
|
||||
PDF_EXPORT_DESKTOP_ONLY: "PDF export is only available on desktop",
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import ExcalidrawView from "src/view/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { fragWithHTML, getExportPadding, getExportTheme, getPNGScale, getWithBackground, shouldEmbedScene } from "src/utils/utils";
|
||||
import { PageOrientation, PageSize, PDFMargin, PDFPageAlignment, PDFPageMarginString, STANDARD_PAGE_SIZES, exportSVGToClipboard } from "src/utils/exportUtils";
|
||||
import { PageOrientation, PageSize, PDFPageAlignment, PDFPageMarginString, exportSVGToClipboard } from "src/utils/exportUtils";
|
||||
import { t } from "src/lang/helpers";
|
||||
import { PDFExportSettings, PDFExportSettingsComponent } from "./PDFExportSettingsComponent";
|
||||
|
||||
@@ -43,7 +43,6 @@ export class ExportDialog extends Modal {
|
||||
public customPaperColor: string = "#ffffff";
|
||||
public alignment: PDFPageAlignment = "center";
|
||||
public margin: PDFPageMarginString = "normal";
|
||||
public exportDPI: number = 300;
|
||||
|
||||
constructor(
|
||||
private plugin: ExcalidrawPlugin,
|
||||
@@ -69,7 +68,6 @@ export class ExportDialog extends Modal {
|
||||
this.customPaperColor = plugin.settings.pdfSettings.customPaperColor;
|
||||
this.alignment = plugin.settings.pdfSettings.alignment;
|
||||
this.margin = plugin.settings.pdfSettings.margin;
|
||||
this.exportDPI = plugin.settings.pdfSettings.exportDPI ?? 300;
|
||||
|
||||
this.saveSettings = false;
|
||||
}
|
||||
@@ -279,7 +277,6 @@ export class ExportDialog extends Modal {
|
||||
customPaperColor: this.customPaperColor,
|
||||
alignment: this.alignment,
|
||||
margin: this.margin,
|
||||
exportDPI: this.exportDPI,
|
||||
};
|
||||
|
||||
new PDFExportSettingsComponent(
|
||||
@@ -293,7 +290,6 @@ export class ExportDialog extends Modal {
|
||||
this.customPaperColor = pdfSettings.customPaperColor;
|
||||
this.alignment = pdfSettings.alignment;
|
||||
this.margin = pdfSettings.margin;
|
||||
this.exportDPI = pdfSettings.exportDPI ?? 300;
|
||||
}
|
||||
).render();
|
||||
}
|
||||
@@ -383,26 +379,11 @@ export class ExportDialog extends Modal {
|
||||
customPaperColor: this.customPaperColor,
|
||||
alignment: this.alignment,
|
||||
margin: this.margin,
|
||||
exportDPI: this.exportDPI,
|
||||
};
|
||||
await this.plugin.saveSettings();
|
||||
new Notice(t("EXPORTDIALOG_SAVE_CONFIRMATION"));
|
||||
};
|
||||
|
||||
const bPDFVault = this.buttonContainerRow1.createEl("button", {
|
||||
text: t("EXPORTDIALOG_PDFTOVAULT"),
|
||||
cls: "excalidraw-export-button"
|
||||
});
|
||||
bPDFVault.onclick = () => {
|
||||
this.view.exportPDF(
|
||||
true,
|
||||
this.hasSelectedElements && this.exportSelectedOnly,
|
||||
this.pageSize,
|
||||
this.pageOrientation
|
||||
);
|
||||
this.close();
|
||||
};
|
||||
|
||||
if (!DEVICE.isDesktop) return;
|
||||
const bPDFExport = this.buttonContainerRow1.createEl("button", {
|
||||
text: t("EXPORTDIALOG_PDF"),
|
||||
|
||||
@@ -10,7 +10,6 @@ export interface PDFExportSettings {
|
||||
customPaperColor: string;
|
||||
alignment: PDFPageAlignment;
|
||||
margin: PDFPageMarginString;
|
||||
exportDPI: number;
|
||||
}
|
||||
|
||||
export class PDFExportSettingsComponent {
|
||||
@@ -55,23 +54,6 @@ export class PDFExportSettingsComponent {
|
||||
this.update();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName(t("EXPORTDIALOG_PDF_DPI"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOptions({
|
||||
"150": "150",
|
||||
"300": "300",
|
||||
"600": "600",
|
||||
"1200": "1200"
|
||||
})
|
||||
.setValue(`${this.settings.exportDPI}`)
|
||||
.onChange(value => {
|
||||
this.settings.exportDPI = parseInt(value);
|
||||
this.update();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName(t("EXPORTDIALOG_PDF_FIT_TO_PAGE"))
|
||||
|
||||
@@ -976,16 +976,17 @@ export class ExcalidrawAutomate {
|
||||
SVG,
|
||||
scale = { fitToPage: 1, zoom: 1 },
|
||||
pageProps,
|
||||
filename,
|
||||
}: {
|
||||
SVG: SVGSVGElement[];
|
||||
scale?: PDFExportScale;
|
||||
pageProps?: PDFPageProperties;
|
||||
}): Promise<ArrayBuffer> {
|
||||
filename: string;
|
||||
}): Promise<void> {
|
||||
if(!pageProps) {
|
||||
pageProps = {
|
||||
alignment: this.plugin.settings.pdfSettings.alignment,
|
||||
margin: getMarginValue(this.plugin.settings.pdfSettings.margin),
|
||||
exportDPI: this.plugin.settings.pdfSettings.exportDPI ?? 300,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -999,7 +1000,7 @@ export class ExcalidrawAutomate {
|
||||
pageProps.backgroundColor = "#ffffff";
|
||||
}
|
||||
|
||||
return await exportToPDF({SVG, scale, pageProps});
|
||||
await exportToPDF({SVG, scale, pageProps, filename});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+300
-297
@@ -1,19 +1,8 @@
|
||||
import { Notice } from 'obsidian';
|
||||
import { getEA } from 'src/core';
|
||||
import { DEVICE } from 'src/constants/constants';
|
||||
import { t } from 'src/lang/helpers';
|
||||
|
||||
// Define proper types for PDFLib
|
||||
type PDFLibType = typeof import('@cantoo/pdf-lib');
|
||||
let pdfLibPromise: Promise<PDFLibType> | null = null;
|
||||
|
||||
async function getPDFLib(): Promise<PDFLibType> {
|
||||
if (!pdfLibPromise) {
|
||||
pdfLibPromise = import('@cantoo/pdf-lib');
|
||||
}
|
||||
return pdfLibPromise;
|
||||
}
|
||||
|
||||
const PDF_DPI = 72;
|
||||
const DPI = 96;
|
||||
|
||||
export type PDFPageAlignment = "center" | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
|
||||
export type PDFPageMarginString = "none" | "tiny" | "normal";
|
||||
@@ -35,7 +24,6 @@ export interface PDFPageProperties {
|
||||
backgroundColor?: string;
|
||||
margin: PDFMargin;
|
||||
alignment: PDFPageAlignment;
|
||||
exportDPI: number;
|
||||
}
|
||||
|
||||
export interface PageDimensions {
|
||||
@@ -76,15 +64,246 @@ export function getPageDimensions(pageSize: PageSize, orientation: PageOrientati
|
||||
: { width: dimensions.height, height: dimensions.width };
|
||||
}
|
||||
|
||||
interface SVGDimensions {
|
||||
width: number;
|
||||
height: number;
|
||||
x: number;
|
||||
y: number;
|
||||
sourceX?: number;
|
||||
sourceY?: number;
|
||||
sourceWidth?: number;
|
||||
sourceHeight?: number;
|
||||
// Electron IPC interfaces
|
||||
interface PrintToPDFOptions {
|
||||
includeName: boolean;
|
||||
pageSize: string | { width: number; height: number };
|
||||
landscape: boolean;
|
||||
margins: { top: number; left: number; right: number; bottom: number };
|
||||
scaleFactor: number;
|
||||
scale: number;
|
||||
open: boolean;
|
||||
filepath: string;
|
||||
}
|
||||
|
||||
interface SaveDialogOptions {
|
||||
defaultPath: string;
|
||||
filters: { name: string; extensions: string[] }[];
|
||||
properties: string[];
|
||||
}
|
||||
|
||||
interface SaveDialogReturnValue {
|
||||
canceled: boolean;
|
||||
filePath?: string;
|
||||
}
|
||||
|
||||
interface ElectronAPI {
|
||||
ipcRenderer: {
|
||||
send(channel: string, ...args: any[]): void;
|
||||
once(channel: string, func: (...args: any[]) => void): void;
|
||||
};
|
||||
remote: {
|
||||
dialog: {
|
||||
showSaveDialog(options: SaveDialogOptions): Promise<SaveDialogReturnValue>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: ElectronAPI;
|
||||
}
|
||||
}
|
||||
|
||||
function getPageSizePixels(pageSize: PageSize | PageDimensions, landscape = false): PageDimensions {
|
||||
if (typeof pageSize === "object") return pageSize;
|
||||
|
||||
const pageDimensions = STANDARD_PAGE_SIZES[pageSize];
|
||||
if (!pageDimensions) {
|
||||
throw new Error(`Unsupported page size: ${pageSize}`);
|
||||
}
|
||||
|
||||
return landscape
|
||||
? { width: pageDimensions.height, height: pageDimensions.width }
|
||||
: { width: pageDimensions.width, height: pageDimensions.height };
|
||||
}
|
||||
|
||||
function getPageSize(pageSize: PageSize | PageDimensions): string | { width: number; height: number } {
|
||||
if (typeof pageSize === "string") return pageSize;
|
||||
|
||||
if (!pageSize || typeof pageSize !== "object" ||
|
||||
typeof pageSize.width !== "number" || typeof pageSize.height !== "number") {
|
||||
throw new Error("Invalid page dimensions");
|
||||
}
|
||||
|
||||
return {
|
||||
width: (pageSize.width / DPI),
|
||||
height: (pageSize.height / DPI)
|
||||
};
|
||||
}
|
||||
|
||||
async function getSavePath(defaultPath: string): Promise<string | undefined> {
|
||||
const result = await window.electron.remote.dialog.showSaveDialog({
|
||||
defaultPath,
|
||||
filters: [
|
||||
{ name: "PDF Files", extensions: ["pdf"] },
|
||||
{ name: "All Files", extensions: ["*"] }
|
||||
],
|
||||
properties: ["showOverwriteConfirmation"]
|
||||
});
|
||||
return result.filePath;
|
||||
}
|
||||
|
||||
async function printPdf(
|
||||
elementToPrint: HTMLElement,
|
||||
pdfPath: string,
|
||||
bgColor: string,
|
||||
pageSize: PageSize | PageDimensions,
|
||||
isLandscape: boolean,
|
||||
margins: { top: number; left: number; right: number; bottom: number }
|
||||
): Promise<void> {
|
||||
const styleTag = document.createElement('style');
|
||||
styleTag.textContent = `
|
||||
@media print {
|
||||
.print {
|
||||
background-color: ${bgColor} !important;
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
align-items: center !important;
|
||||
-webkit-print-color-adjust: exact !important;
|
||||
print-color-adjust: exact !important;
|
||||
flex-direction: column !important;
|
||||
page-break-before: always;
|
||||
margin: 0px !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(styleTag);
|
||||
|
||||
const printDiv = document.body.createDiv('print');
|
||||
printDiv.style.top = "0";
|
||||
printDiv.style.left = "0";
|
||||
printDiv.style.display = "flex";
|
||||
printDiv.appendChild(elementToPrint);
|
||||
|
||||
const options: PrintToPDFOptions = {
|
||||
includeName: false,
|
||||
pageSize: getPageSize(pageSize),
|
||||
landscape: isLandscape,
|
||||
margins,
|
||||
scaleFactor: 100,
|
||||
scale: 1,
|
||||
open: true,
|
||||
filepath: pdfPath,
|
||||
};
|
||||
|
||||
try {
|
||||
await new Promise<void>((resolve) => {
|
||||
window.electron.ipcRenderer.once('print-to-pdf', resolve);
|
||||
window.electron.ipcRenderer.send('print-to-pdf', options);
|
||||
});
|
||||
} finally {
|
||||
printDiv.remove();
|
||||
styleTag.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function calculateDimensions(
|
||||
svgWidth: number,
|
||||
svgHeight: number,
|
||||
pageDim: PageDimensions,
|
||||
margin: PDFMargin,
|
||||
scale: PDFExportScale,
|
||||
alignment: PDFPageAlignment
|
||||
): {
|
||||
tiles: {
|
||||
viewBox: string,
|
||||
width: number,
|
||||
height: number,
|
||||
x: number,
|
||||
y: number
|
||||
}[],
|
||||
pages: number
|
||||
} {
|
||||
const availableWidth = pageDim.width - margin.left - margin.right;
|
||||
const availableHeight = pageDim.height - margin.top - margin.bottom;
|
||||
|
||||
// If fitToPage is specified, find optimal zoom using binary search
|
||||
if (scale.fitToPage > 0) {
|
||||
let low = 0;
|
||||
let high = 100;
|
||||
let bestZoom = 1;
|
||||
const tolerance = 0.000001;
|
||||
|
||||
while (high - low > tolerance) {
|
||||
const mid = (low + high) / 2;
|
||||
const scaledWidth = svgWidth * mid;
|
||||
const scaledHeight = svgHeight * mid;
|
||||
const pages = Math.ceil(scaledWidth / availableWidth) *
|
||||
Math.ceil(scaledHeight / availableHeight);
|
||||
|
||||
if (pages > scale.fitToPage) {
|
||||
high = mid;
|
||||
} else {
|
||||
bestZoom = mid;
|
||||
low = mid;
|
||||
}
|
||||
}
|
||||
scale.zoom = Math.round(bestZoom * 0.99999 * 1000000) / 1000000;
|
||||
}
|
||||
|
||||
const finalWidth = svgWidth * (scale.zoom || 1);
|
||||
const finalHeight = svgHeight * (scale.zoom || 1);
|
||||
|
||||
if (finalWidth <= availableWidth && finalHeight <= availableHeight) {
|
||||
// Content fits on one page
|
||||
const position = calculatePosition(
|
||||
finalWidth,
|
||||
finalHeight,
|
||||
pageDim.width,
|
||||
pageDim.height,
|
||||
margin,
|
||||
alignment
|
||||
);
|
||||
|
||||
return {
|
||||
tiles: [{
|
||||
viewBox: `0 0 ${svgWidth} ${svgHeight}`,
|
||||
width: finalWidth,
|
||||
height: finalHeight,
|
||||
x: position.x,
|
||||
y: position.y
|
||||
}],
|
||||
pages: 1
|
||||
};
|
||||
}
|
||||
|
||||
// Content needs to be tiled
|
||||
const tiles = [];
|
||||
const cols = Math.ceil(finalWidth / availableWidth);
|
||||
const rows = Math.ceil(finalHeight / availableHeight);
|
||||
|
||||
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 position for each tile
|
||||
const position = calculatePosition(
|
||||
scaledTileWidth,
|
||||
scaledTileHeight,
|
||||
pageDim.width,
|
||||
pageDim.height,
|
||||
margin,
|
||||
alignment
|
||||
);
|
||||
|
||||
tiles.push({
|
||||
viewBox: `${tileX} ${tileY} ${tileWidth} ${tileHeight}`,
|
||||
width: scaledTileWidth,
|
||||
height: scaledTileHeight,
|
||||
x: position.x,
|
||||
y: position.y
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { tiles, pages: tiles.length };
|
||||
}
|
||||
|
||||
function calculatePosition(
|
||||
@@ -99,7 +318,7 @@ function calculatePosition(
|
||||
const availableHeight = pageHeight - margin.top - margin.bottom;
|
||||
|
||||
let x = margin.left;
|
||||
let y = margin.bottom;
|
||||
let y = margin.top;
|
||||
|
||||
// Handle horizontal alignment
|
||||
if (alignment.includes('center')) {
|
||||
@@ -110,308 +329,92 @@ function calculatePosition(
|
||||
|
||||
// Handle vertical alignment
|
||||
if (alignment.startsWith('center')) {
|
||||
y = margin.bottom + (availableHeight - svgHeight) / 2;
|
||||
} else if (alignment.startsWith('top')) {
|
||||
y = margin.bottom;
|
||||
y = margin.top + (availableHeight - svgHeight) / 2;
|
||||
} else if (alignment.startsWith('bottom')) {
|
||||
y = pageHeight - margin.top - svgHeight;
|
||||
y = margin.top + availableHeight - svgHeight;
|
||||
}
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
function getNumberOfPages(
|
||||
width: number,
|
||||
height: number,
|
||||
availableWidth: number,
|
||||
availableHeight: number
|
||||
): number {
|
||||
const cols = Math.ceil(width / availableWidth);
|
||||
const rows = Math.ceil(height / availableHeight);
|
||||
return cols * rows;
|
||||
}
|
||||
|
||||
function calculateDimensions(
|
||||
svgWidth: number,
|
||||
svgHeight: number,
|
||||
pageDim: PageDimensions,
|
||||
margin: PDFPageProperties['margin'],
|
||||
scale: PDFExportScale,
|
||||
alignment: PDFPageAlignment,
|
||||
exportDPI: number,
|
||||
): SVGDimensions[] {
|
||||
const svg_to_pdf_scale = PDF_DPI / exportDPI;
|
||||
const pdfWidth = svgWidth * svg_to_pdf_scale;
|
||||
const pdfHeight = svgHeight * svg_to_pdf_scale;
|
||||
const availableWidth = pageDim.width - margin.left - margin.right;
|
||||
const availableHeight = pageDim.height - margin.top - margin.bottom;
|
||||
|
||||
// If fitToPage is specified, find optimal zoom using binary search
|
||||
if (scale.fitToPage > 0) {
|
||||
let low = 0;
|
||||
let high = 100; // Start with a reasonable upper bound
|
||||
let bestZoom = 1;
|
||||
const tolerance = 0.000001;
|
||||
|
||||
while (high - low > tolerance) {
|
||||
const mid = (low + high) / 2;
|
||||
const scaledWidth = pdfWidth * mid;
|
||||
const scaledHeight = pdfHeight * mid;
|
||||
const pages = getNumberOfPages(scaledWidth, scaledHeight, availableWidth, availableHeight);
|
||||
|
||||
if (pages > scale.fitToPage) {
|
||||
high = mid;
|
||||
} else {
|
||||
bestZoom = mid;
|
||||
low = mid;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply a small reduction to prevent floating-point issues
|
||||
scale.zoom = Math.round(bestZoom * 0.99999 * 1000000) / 1000000;
|
||||
}
|
||||
|
||||
// Now handle as regular scale mode
|
||||
const finalWidth = Math.round(pdfWidth * (scale.zoom || 1) * 1000) / 1000;
|
||||
const finalHeight = Math.round(pdfHeight * (scale.zoom || 1) * 1000) / 1000;
|
||||
|
||||
// Round the available dimensions as well for consistent comparison
|
||||
const roundedAvailableWidth = Math.round(availableWidth * 1000) / 1000;
|
||||
const roundedAvailableHeight = Math.round(availableHeight * 1000) / 1000;
|
||||
|
||||
if (finalWidth <= roundedAvailableWidth && finalHeight <= roundedAvailableHeight) {
|
||||
// Content fits on one page
|
||||
const position = calculatePosition(
|
||||
finalWidth,
|
||||
finalHeight,
|
||||
pageDim.width,
|
||||
pageDim.height,
|
||||
margin,
|
||||
alignment,
|
||||
);
|
||||
|
||||
return [{
|
||||
width: finalWidth,
|
||||
height: finalHeight,
|
||||
x: position.x,
|
||||
y: position.y
|
||||
}];
|
||||
} else {
|
||||
// Content needs to be tiled across multiple pages
|
||||
const dimensions: SVGDimensions[] = [];
|
||||
// Calculate exact number of needed columns and rows
|
||||
const cols = Math.ceil(finalWidth / roundedAvailableWidth);
|
||||
const rows = Math.ceil(finalHeight / roundedAvailableHeight);
|
||||
|
||||
for (let row = 0; row < rows; row++) {
|
||||
for (let col = 0; col < cols; col++) {
|
||||
// Calculate remaining width and height for this tile
|
||||
const remainingWidth = finalWidth - col * roundedAvailableWidth;
|
||||
const remainingHeight = finalHeight - row * roundedAvailableHeight;
|
||||
|
||||
// Only create tile if there's actual content to show
|
||||
if (remainingWidth > 0 && remainingHeight > 0) {
|
||||
const tileWidth = Math.min(roundedAvailableWidth, remainingWidth);
|
||||
const tileHeight = Math.min(roundedAvailableHeight, remainingHeight);
|
||||
|
||||
dimensions.push({
|
||||
width: tileWidth,
|
||||
height: tileHeight,
|
||||
x: margin.left,
|
||||
y: margin.top,
|
||||
sourceX: (col * roundedAvailableWidth) / ((scale.zoom || 1) * svg_to_pdf_scale),
|
||||
sourceY: (row * roundedAvailableHeight) / ((scale.zoom || 1) * svg_to_pdf_scale),
|
||||
sourceWidth: tileWidth / ((scale.zoom || 1) * svg_to_pdf_scale),
|
||||
sourceHeight: tileHeight / ((scale.zoom || 1) * svg_to_pdf_scale)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return dimensions;
|
||||
}
|
||||
}
|
||||
|
||||
async function addSVGToPage(
|
||||
pdfDoc: Awaited<ReturnType<typeof import('@cantoo/pdf-lib').PDFDocument.create>>,
|
||||
svg: SVGSVGElement,
|
||||
dimensions: SVGDimensions,
|
||||
pageDim: PageDimensions,
|
||||
pageProps: PDFPageProperties
|
||||
) {
|
||||
const { rgb } = await getPDFLib();
|
||||
const page = pdfDoc.addPage([pageDim.width, pageDim.height]);
|
||||
|
||||
if (pageProps.backgroundColor && pageProps.backgroundColor !== '#ffffff') {
|
||||
const { r, g, b } = hexToRGB(pageProps.backgroundColor);
|
||||
page.drawRectangle({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: pageDim.width,
|
||||
height: pageDim.height,
|
||||
color: rgb(r/255, g/255, b/255),
|
||||
});
|
||||
}
|
||||
|
||||
// Render SVG to canvas with specified DPI
|
||||
const canvas = await renderSVGToCanvas(svg, dimensions, pageProps.exportDPI);
|
||||
|
||||
// Convert canvas to PNG
|
||||
const pngData = canvas.toDataURL('image/png');
|
||||
|
||||
// Embed the PNG in the PDF
|
||||
const image = await pdfDoc.embedPng(pngData);
|
||||
|
||||
// Adjust y-coordinate to account for PDF coordinate system
|
||||
const adjustedY = pageDim.height - dimensions.y - dimensions.height;
|
||||
|
||||
// Draw the image
|
||||
page.drawImage(image, {
|
||||
x: dimensions.x,
|
||||
y: adjustedY,
|
||||
width: dimensions.width,
|
||||
height: dimensions.height,
|
||||
});
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
async function renderSVGToCanvas(
|
||||
svg: SVGSVGElement,
|
||||
dimensions: SVGDimensions,
|
||||
exportDPI: number = 300,
|
||||
): Promise<HTMLCanvasElement> {
|
||||
const canvas = document.createElement('canvas');
|
||||
const scale = exportDPI / PDF_DPI;
|
||||
|
||||
canvas.width = dimensions.width * scale;
|
||||
canvas.height = dimensions.height * scale;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) throw new Error('Failed to get canvas context');
|
||||
|
||||
let svgToRender = svg;
|
||||
if (dimensions.sourceX !== undefined) {
|
||||
svgToRender = svg.cloneNode(true) as SVGSVGElement;
|
||||
const viewBox = `${dimensions.sourceX} ${dimensions.sourceY} ${dimensions.sourceWidth} ${dimensions.sourceHeight}`;
|
||||
svgToRender.setAttribute('viewBox', viewBox);
|
||||
svgToRender.setAttribute('width', String(dimensions.sourceWidth));
|
||||
svgToRender.setAttribute('height', String(dimensions.sourceHeight));
|
||||
}
|
||||
|
||||
const svgBlob = new Blob([svgToRender.outerHTML], { type: 'image/svg+xml;charset=utf-8' });
|
||||
const blobUrl = URL.createObjectURL(svgBlob);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
resolve(canvas);
|
||||
};
|
||||
img.onerror = () => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
reject(new Error('Failed to load SVG'));
|
||||
};
|
||||
img.src = blobUrl;
|
||||
});
|
||||
}
|
||||
|
||||
export async function exportToPDF({
|
||||
SVG,
|
||||
scale = { fitToPage: 1, zoom: 1 },
|
||||
pageProps,
|
||||
filename
|
||||
}: {
|
||||
SVG: SVGSVGElement[];
|
||||
scale: PDFExportScale;
|
||||
pageProps: PDFPageProperties;
|
||||
}): Promise<ArrayBuffer> {
|
||||
const { PDFDocument } = await getPDFLib();
|
||||
const pdfDoc = await PDFDocument.create();
|
||||
filename: string;
|
||||
}): Promise<void> {
|
||||
if (!DEVICE.isDesktop) {
|
||||
new Notice(t("PDF_EXPORT_DESKTOP_ONLY"));
|
||||
return;
|
||||
}
|
||||
|
||||
const msg = t('EXPORTDIALOG_PDF_PROGRESS_NOTICE');
|
||||
const imgmsg = t('EXPORTDIALOG_PDF_PROGRESS_IMAGE');
|
||||
const savePath = await getSavePath(filename);
|
||||
if (!savePath) return;
|
||||
|
||||
let notice = new Notice(msg, 0);
|
||||
//@ts-ignore
|
||||
let noticeContainerEl = notice.containerEl ?? notice.noticeEl;
|
||||
const {width, height} = getPageSizePixels(pageProps.dimensions, false);
|
||||
const allPagesDiv = createDiv();
|
||||
allPagesDiv.style.width = "100%";
|
||||
allPagesDiv.style.height = "fit-content";
|
||||
|
||||
let j=1;
|
||||
let j = 1;
|
||||
for (const svg of SVG) {
|
||||
const svgWidth = parseFloat(svg.getAttribute('width') || '0');
|
||||
const svgHeight = parseFloat(svg.getAttribute('height') || '0');
|
||||
|
||||
const dimensions = calculateDimensions(
|
||||
svgWidth,
|
||||
svgHeight,
|
||||
pageProps.dimensions,
|
||||
pageProps.margin,
|
||||
const {tiles} = calculateDimensions(
|
||||
svgWidth,
|
||||
svgHeight,
|
||||
pageProps.dimensions,
|
||||
pageProps.margin,
|
||||
scale,
|
||||
pageProps.alignment,
|
||||
pageProps.exportDPI
|
||||
pageProps.alignment
|
||||
);
|
||||
|
||||
let i=1;
|
||||
for (const dim of dimensions) {
|
||||
if(noticeContainerEl.parentElement) {
|
||||
notice.setMessage(`${msg} ${i++}/${dimensions.length}${SVG.length>1?` ${imgmsg} ${j}`:""}`);
|
||||
} else {
|
||||
notice = new Notice(`${msg} ${i++}/${dimensions.length}${SVG.length>1?` ${imgmsg} ${j}`:""}`, 0);
|
||||
//@ts-ignore
|
||||
noticeContainerEl = notice.containerEl ?? notice.noticeEl;
|
||||
}
|
||||
await addSVGToPage(pdfDoc, svg, dim, pageProps.dimensions, pageProps);
|
||||
let i = 1;
|
||||
for (const tile of tiles) {
|
||||
const pageDiv = createDiv();
|
||||
pageDiv.style.width = `${width}px`;
|
||||
pageDiv.style.height = `${height}px`;
|
||||
pageDiv.style.display = "flex";
|
||||
pageDiv.style.justifyContent = "start";
|
||||
pageDiv.style.alignItems = "left";
|
||||
pageDiv.style.padding = `${pageProps.margin.top}px ${pageProps.margin.right}px ${pageProps.margin.bottom}px ${pageProps.margin.left}px`;
|
||||
|
||||
const clonedSVG = svg.cloneNode(true) as SVGSVGElement;
|
||||
clonedSVG.setAttribute('viewBox', tile.viewBox);
|
||||
clonedSVG.style.width = `${tile.width}px`;
|
||||
clonedSVG.style.height = `${tile.height}px`;
|
||||
clonedSVG.style.position = 'relative';
|
||||
clonedSVG.style.left = `${tile.x}px`;
|
||||
clonedSVG.style.top = `${tile.y}px`;
|
||||
|
||||
pageDiv.appendChild(clonedSVG);
|
||||
allPagesDiv.appendChild(pageDiv);
|
||||
i++;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
if(noticeContainerEl.parentElement) {
|
||||
notice.setMessage(t('EXPORTDIALOG_PDF_PROGRESS_DONE'));
|
||||
setTimeout(() => notice.hide(), 4000);
|
||||
} else {
|
||||
new Notice(t('EXPORTDIALOG_PDF_PROGRESS_DONE'));
|
||||
new Notice(t("EXPORTDIALOG_PDF_PROGRESS_NOTICE"));
|
||||
try {
|
||||
await printPdf(
|
||||
allPagesDiv,
|
||||
savePath,
|
||||
pageProps.backgroundColor || "#ffffff",
|
||||
pageProps.dimensions,
|
||||
false,
|
||||
{ top: 0, right: 0, bottom: 0, left: 0 }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Failed to export to PDF: ", error);
|
||||
new Notice(t("EXPORTDIALOG_PDF_PROGRESS_ERROR"));
|
||||
}
|
||||
return pdfDoc.save();
|
||||
}
|
||||
|
||||
function hexToRGB(hex: string): { r: number; g: number; b: number } {
|
||||
const ea = getEA();
|
||||
const color = ea.getCM(hex);
|
||||
if (color) {
|
||||
return { r: color.red, g: color.green, b: color.blue };
|
||||
}
|
||||
return {r: 255, g: 255, b: 255};
|
||||
}
|
||||
|
||||
// Helper function to split SVG into pages if needed
|
||||
function splitSVGIntoPages(
|
||||
svg: SVGSVGElement,
|
||||
maxWidth: number,
|
||||
maxHeight: number
|
||||
): SVGSVGElement[] {
|
||||
const width = parseFloat(svg.getAttribute('width') || '0');
|
||||
const height = parseFloat(svg.getAttribute('height') || '0');
|
||||
|
||||
if (width <= maxWidth && height <= maxHeight) {
|
||||
return [svg];
|
||||
}
|
||||
|
||||
const pages: SVGSVGElement[] = [];
|
||||
const cols = Math.ceil(width / maxWidth);
|
||||
const rows = Math.ceil(height / maxHeight);
|
||||
|
||||
for (let row = 0; row < rows; row++) {
|
||||
for (let col = 0; col < cols; col++) {
|
||||
const viewBox = `${col * maxWidth} ${row * maxHeight} ${maxWidth} ${maxHeight}`;
|
||||
const clonedSvg = svg.cloneNode(true) as SVGSVGElement;
|
||||
clonedSvg.setAttribute('viewBox', viewBox);
|
||||
clonedSvg.setAttribute('width', String(maxWidth));
|
||||
clonedSvg.setAttribute('height', String(maxHeight));
|
||||
pages.push(clonedSvg);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
new Notice(t("EXPORTDIALOG_PDF_PROGRESS_DONE"));
|
||||
}
|
||||
|
||||
export async function exportSVGToClipboard(svg: SVGSVGElement) {
|
||||
|
||||
@@ -587,7 +587,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
return;
|
||||
}
|
||||
|
||||
const pdfArrayBuffer = await exportToPDF({
|
||||
exportToPDF({
|
||||
SVG: [svg],
|
||||
scale: {
|
||||
zoom: this.exportDialog.scale,
|
||||
@@ -598,36 +598,9 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
backgroundColor: this.exportDialog.getPaperColor(),
|
||||
margin: getMarginValue(this.exportDialog.margin),
|
||||
alignment: this.exportDialog.alignment,
|
||||
exportDPI: this.exportDialog.exportDPI,
|
||||
}
|
||||
},
|
||||
filename: this.file.basename,
|
||||
});
|
||||
|
||||
if (!pdfArrayBuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(toVault) {
|
||||
const filepath = getIMGFilename(this.file.path, "pdf");
|
||||
const file = await createOrOverwriteFile(this.app, filepath, pdfArrayBuffer);
|
||||
let leaf: WorkspaceLeaf;
|
||||
this.app.workspace.getLeavesOfType("pdf").forEach((l) => {
|
||||
//@ts-ignore
|
||||
if(l.view?.file === file) {
|
||||
leaf = l;
|
||||
}
|
||||
});
|
||||
if(leaf) {
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
} else {
|
||||
this.app.workspace.getLeaf("split").openFile(file);
|
||||
}
|
||||
} else {
|
||||
download(
|
||||
"data:application/pdf;base64",
|
||||
arrayBufferToBase64(pdfArrayBuffer),
|
||||
`${this.file.basename}.pdf`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async png(scene: any, theme?:string, embedScene?: boolean): Promise<Blob> {
|
||||
|
||||
Reference in New Issue
Block a user