mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
5 Commits
2.8.0-beta
...
printToPDF
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23b94da8f0 | ||
|
|
1f227ddd24 | ||
|
|
12152665af | ||
|
|
064e17b29d | ||
|
|
0aaba80c82 |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.8.0-beta-4",
|
||||
"version": "2.8.0-rc-1",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
123
package-lock.json
generated
123
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -26,7 +26,7 @@ export const ERROR_IFRAME_CONVERSION_CANCELED = "iframe conversion canceled";
|
||||
|
||||
declare const excalidrawLib: typeof ExcalidrawLib;
|
||||
|
||||
export const LOCALE = moment.locale();
|
||||
export const LOCALE = localStorage.getItem("language")?.toLowerCase() || "en";
|
||||
export const CJK_FONTS = "CJK Fonts";
|
||||
|
||||
export const obsidianToExcalidrawMap: { [key: string]: string } = {
|
||||
@@ -446,4 +446,4 @@ export const SCRIPTENGINE_ICON = `<g transform="translate(-8,-8)"><path d="M24.3
|
||||
export const DISK_ICON_NAME = "save";
|
||||
export const EXPORT_IMG_ICON = ` <g transform="scale(4.166)" strokeWidth="1.25" fill="none" stroke="currentColor"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M15 8h.01"></path><path d="M12 20h-5a3 3 0 0 1 -3 -3v-10a3 3 0 0 1 3 -3h10a3 3 0 0 1 3 3v5"></path><path d="M4 15l4 -4c.928 -.893 2.072 -.893 3 0l4 4"></path><path d="M14 14l1 -1c.617 -.593 1.328 -.793 2.009 -.598"></path><path d="M19 16v6"></path><path d="M22 19l-3 3l-3 -3"></path></g>`;
|
||||
export const EXPORT_IMG_ICON_NAME = `export-img`;
|
||||
export const EXCALIDRAW_ICON = `<path d="M24 17h121v121H24z" style="fill:none" transform="matrix(.8843 0 0 .83471 -21.223 -14.19)"/><path d="M119.81 105.98a.549.549 0 0 0-.53-.12c-4.19-6.19-9.52-12.06-14.68-17.73l-.85-.93c0-.11-.05-.21-.12-.3a.548.548 0 0 0-.34-.2l-.17-.18-.12-.09c-.15-.32-.53-.56-.95-.35-1.58.81-3 1.97-4.4 3.04-1.87 1.43-3.7 2.92-5.42 4.52-.7.65-1.39 1.33-1.97 2.09-.28.37-.07.72.27.87-1.22 1.2-2.45 2.45-3.68 3.74-.11.12-.17.28-.16.44.01.16.09.31.22.41l2.16 1.65s.01.03.03.04c3.09 3.05 8.51 7.28 14.25 11.76.85.67 1.71 1.34 2.57 2.01.39.47.76.94 1.12 1.4.19.25.55.3.8.11.13.1.26.21.39.31a.57.57 0 0 0 .8-.1c.07-.09.1-.2.11-.31.04 0 .07.03.1.03.15 0 .31-.06.42-.18l10.18-11.12a.56.56 0 0 0-.04-.8l.01-.01Zm-29.23-3.85c.07.09.14.17.21.25 1.16.98 2.4 2.04 3.66 3.12l-5.12-3.91s-.32-.22-.52-.36c-.11-.08-.21-.16-.31-.24l-.38-.32s.07-.07.1-.11l.35-.35c1.72-1.74 4.67-4.64 6.19-6.06-1.61 1.62-4.87 6.37-4.17 7.98h-.01Zm17.53 13.81-4.22-3.22c-1.65-1.71-3.43-3.4-5.24-5.03 2.28 1.76 4.23 3.25 4.52 3.51 2.21 1.97 2.11 1.61 3.63 2.91l1.83 1.33c-.18.16-.36.33-.53.49l.01.01Zm1.06.81-.08-.06c.16-.13.33-.25.49-.38l-.4.44h-.01Zm-66.93-65.3c.14.72.27 1.43.4 2.11.69 3.7 1.33 7.03 2.55 9.56l.48 1.92c.19.73.46 1.64.71 1.83 2.85 2.52 7.22 6.28 11.89 9.82.21.16.5.15.7-.01.01.02.03.03.04.04.11.1.24.15.38.15.16 0 .31-.06.42-.19 5.98-6.65 10.43-12.12 13.6-16.7.2-.25.3-.54.29-.84.2-.24.41-.48.6-.68a.558.558 0 0 0-.1-.86.578.578 0 0 0-.17-.36c-1.39-1.34-2.42-2.31-3.46-3.28-1.84-1.72-3.74-3.5-7.77-7.51-.02-.02-.05-.04-.07-.06a.555.555 0 0 0-.22-.14c-1.11-.39-3.39-.78-6.26-1.28-4.22-.72-10-1.72-15.2-3.27h-.04v-.01s-.02 0-.03.02h-.01l.04-.02s-.31.01-.37.04c-.08.04-.14.09-.19.15-.05.06-.09.12-.47.2-.38.08.08 0 .11 0h-.11v.03c.07.34.05.58.16.97-.02.1.21 1.02.24 1.11l1.83 7.26h.03Zm30.95 6.54s-.03.04-.04.05l-.64-.71c.22.21.44.42.68.66Zm-7.09 9.39s-.07.08-.1.12l-.02-.02c.04-.03.08-.07.13-.1h-.01Zm-7.07 8.47Zm3.02-28.57c.35.35 1.74 1.65 2.06 1.97-1.45-.66-5.06-2.34-6.74-2.88 1.65.29 3.93.66 4.68.91Zm-19.18-2.77c.84 1.44 1.5 6.49 2.16 11.4-.37-1.58-.69-3.12-.99-4.6-.52-2.56-1-4.85-1.67-6.88.14.01.31.03.49.05 0 .01 0 .02.02.03h-.01Zm-.29-1.21c-.23-.02-.44-.04-.62-.05-.02-.04-.03-.08-.04-.12l.66.18v-.01Zm-2.22.45v-.02.02Zm78.54-1.18c.04-.23-1.1-1.24-.74-1.26.85-.04.86-1.35 0-1.31-1.13.06-2.27.32-3.37.53-1.98.37-3.95.78-5.92 1.21-4.39.94-8.77 1.93-13.1 3.11-1.36.37-2.86.7-4.11 1.36-.42.22-.4.67-.17.95-.09.05-.18.08-.28.09-.37.07-.74.13-1.11.19a.566.566 0 0 0-.39.86c-2.32 3.1-4.96 6.44-7.82 9.95-2.81 3.21-5.73 6.63-8.72 10.14-9.41 11.06-20.08 23.6-31.9 34.64-.23.21-.24.57-.03.8.05.06.12.1.19.13-.16.15-.32.3-.48.44-.1.09-.14.2-.16.32-.08.08-.16.17-.23.25-.21.23-.2.59.03.8.23.21.59.2.8-.03.04-.04.08-.09.12-.13a.84.84 0 0 1 1.22 0c.69.74 1.34 1.44 1.95 2.09l-1.38-1.15a.57.57 0 0 0-.8.07c-.2.24-.17.6.07.8l14.82 12.43c.11.09.24.13.37.13.15 0 .29-.06.4-.17l.36-.36a.56.56 0 0 0 .63-.12c20.09-20.18 36.27-35.43 54.8-49.06.17-.12.25-.32.23-.51a.57.57 0 0 0 .48-.39c3.42-10.46 4.08-19.72 4.28-24.27 0-.03.01-.05.02-.07.02-.05.03-.1.04-.14.03-.11.05-.19.05-.19.26-.78.17-1.53-.15-2.15v.02ZM82.98 58.94c.9-1.03 1.79-2.04 2.67-3.02-5.76 7.58-15.3 19.26-28.81 33.14 9.2-10.18 18.47-20.73 26.14-30.12Zm-32.55 52.81-.03-.03c.11.02.19.04.2.04a.47.47 0 0 0-.17 0v-.01Zm6.9 6.42-.05-.04.03-.03c.02 0 .03.02.04.02 0 .02-.02.03-.03.05h.01Zm8.36-7.21 1.38-1.44c.01.01.02.03.03.05-.47.46-.94.93-1.42 1.39h.01Zm2.24-2.21c.26-.3.56-.65.87-1.02.01-.01.02-.03.04-.04 3.29-3.39 6.68-6.82 10.18-10.25.02-.02.05-.04.07-.06.86-.66 1.82-1.39 2.72-2.08-4.52 4.32-9.11 8.78-13.88 13.46v-.01Zm21.65-55.88c-1.86 2.42-3.9 5.56-5.63 8.07-5.46 7.91-23.04 27.28-23.43 27.65-2.71 2.62-10.88 10.46-16.09 15.37-.14.13-.25.24-.34.35a.794.794 0 0 1 .03-1.13c24.82-23.4 39.88-42.89 46-51.38-.13.33-.24.69-.55 1.09l.01-.02Zm16.51 7.1-.01.02c0-.02-.02-.07.01-.02Zm-.91-5.13Zm-5.89 9.45c-2.26-1.31-3.32-3.27-2.71-5.25l.19-.66c.08-.19.17-.38.28-.57.59-.98 1.49-1.85 2.52-2.36.05-.02.1-.03.15-.04a.795.795 0 0 1-.04-.43c.05-.31.25-.58.66-.58.67 0 2.75.62 3.54 1.3.24.19.47.4.68.63.3.35.74.92.96 1.33.13.06.23.62.38.91.14.46.2.93.18 1.4 0 .02 0 .02.01.03-.03.07 0 .37-.04.4-.1.72-.36 1.43-.75 2.05-.04.05-.07.11-.11.16 0 .01-.02.02-.03.04-.3.43-.65.83-1.08 1.13-1.26.89-2.73 1.16-4.2.79a6.33 6.33 0 0 1-.57-.25l-.02-.03Zm16.27-1.63c-.49 2.05-1.09 4.19-1.8 6.38-.03.08-.03.16-.03.23-.1.01-.19.05-.27.11-4.44 3.26-8.73 6.62-12.98 10.11 3.67-3.32 7.39-6.62 11.23-9.95a6.409 6.409 0 0 0 2.11-3.74l.56-3.37.03-.1c.25-.71 1.34-.4 1.17.33h-.02Z" style="fill:currentColor;fill-rule:nonzero" transform="translate(-26.41 -29.49)"/>`;
|
||||
export const EXCALIDRAW_ICON = `<path d="M24 17h121v121H24z" style="fill:none" transform="matrix(.8843 0 0 .83471 -21.223 -14.19)"/><path d="M119.81 105.98a.549.549 0 0 0-.53-.12c-4.19-6.19-9.52-12.06-14.68-17.73l-.85-.93c0-.11-.05-.21-.12-.3a.548.548 0 0 0-.34-.2l-.17-.18-.12-.09c-.15-.32-.53-.56-.95-.35-1.58.81-3 1.97-4.4 3.04-1.87 1.43-3.7 2.92-5.42 4.52-.7.65-1.39 1.33-1.97 2.09-.28.37-.07.72.27.87-1.22 1.2-2.45 2.45-3.68 3.74-.11.12-.17.28-.16.44.01.16.09.31.22.41l2.16 1.65s.01.03.03.04c3.09 3.05 8.51 7.28 14.25 11.76.85.67 1.71 1.34 2.57 2.01.39.47.76.94 1.12 1.4.19.25.55.3.8.11.13.1.26.21.39.31a.57.57 0 0 0 .8-.1c.07-.09.1-.2.11-.31.04 0 .07.03.1.03.15 0 .31-.06.42-.18l10.18-11.12a.56.56 0 0 0-.04-.8l.01-.01Zm-29.23-3.85c.07.09.14.17.21.25 1.16.98 2.4 2.04 3.66 3.12l-5.12-3.91s-.32-.22-.52-.36c-.11-.08-.21-.16-.31-.24l-.38-.32s.07-.07.1-.11l.35-.35c1.72-1.74 4.67-4.64 6.19-6.06-1.61 1.62-4.87 6.37-4.17 7.98h-.01Zm17.53 13.81-4.22-3.22c-1.65-1.71-3.43-3.4-5.24-5.03 2.28 1.76 4.23 3.25 4.52 3.51 2.21 1.97 2.11 1.61 3.63 2.91l1.83 1.33c-.18.16-.36.33-.53.49l.01.01Zm1.06.81-.08-.06c.16-.13.33-.25.49-.38l-.4.44h-.01Zm-66.93-65.3c.14.72.27 1.43.4 2.11.69 3.7 1.33 7.03 2.55 9.56l.48 1.92c.19.73.46 1.64.71 1.83 2.85 2.52 7.22 6.28 11.89 9.82.21.16.5.15.7-.01.01.02.03.03.04.04.11.1.24.15.38.15.16 0 .31-.06.42-.19 5.98-6.65 10.43-12.12 13.6-16.7.2-.25.3-.54.29-.84.2-.24.41-.48.6-.68a.558.558 0 0 0-.1-.86.578.578 0 0 0-.17-.36c-1.39-1.34-2.42-2.31-3.46-3.28-1.84-1.72-3.74-3.5-7.77-7.51-.02-.02-.05-.04-.07-.06a.555.555 0 0 0-.22-.14c-1.11-.39-3.39-.78-6.26-1.28-4.22-.72-10-1.72-15.2-3.27h-.04v-.01s-.02 0-.03.02h-.01l.04-.02s-.31.01-.37.04c-.08.04-.14.09-.19.15-.05.06-.09.12-.47.2-.38.08.08 0 .11 0h-.11v.03c.07.34.05.58.16.97-.02.1.21 1.02.24 1.11l1.83 7.26h.03Zm30.95 6.54s-.03.04-.04.05l-.64-.71c.22.21.44.42.68.66Zm-7.09 9.39s-.07.08-.1.12l-.02-.02c.04-.03.08-.07.13-.1h-.01Zm-7.07 8.47Zm3.02-28.57c.35.35 1.74 1.65 2.06 1.97-1.45-.66-5.06-2.34-6.74-2.88 1.65.29 3.93.66 4.68.91Zm-19.18-2.77c.84 1.44 1.5 6.49 2.16 11.4-.37-1.58-.69-3.12-.99-4.6-.52-2.56-1-4.85-1.67-6.88.14.01.31.03.49.05 0 .01 0 .02.02.03h-.01Zm-.29-1.21c-.23-.02-.44-.04-.62-.05-.02-.04-.03-.08-.04-.12l.66.18v-.01Zm-2.22.45v-.02.02Zm78.54-1.18c.04-.23-1.1-1.24-.74-1.26.85-.04.86-1.35 0-1.31-1.13.06-2.27.32-3.37.53-1.98.37-3.95.78-5.92 1.21-4.39.94-8.77 1.93-13.1 3.11-1.36.37-2.86.7-4.11 1.36-.42.22-.4.67-.17.95-.09.05-.18.08-.28.09-.37.07-.74.13-1.11.19a.566.566 0 0 0-.39.86c-2.32 3.1-4.96 6.44-7.82 9.95-2.81 3.21-5.73 6.63-8.72 10.14-9.41 11.06-20.08 23.6-31.9 34.64-.23.21-.24.57-.03.8.05.06.12.1.19.13-.16.15-.32.3-.48.44-.1.09-.14.2-.16.32-.08.08-.16.17-.23.25-.21.23-.2.59.03.8.23.21.59.2.8-.03.04-.04.08-.09.12-.13a.84.84 0 0 1 1.22 0c.69.74 1.34 1.44 1.95 2.09l-1.38-1.15a.57.57 0 0 0-.8.07c-.2.24-.17.6.07.8l14.82 12.43c.11.09.24.13.37.13.15 0 .29-.06.4-.17l.36-.36a.56.56 0 0 0 .63-.12c20.09-20.18 36.27-35.43 54.8-49.06.17-.12.25-.32.23-.51a.57.57 0 0 0 .48-.39c3.42-10.46 4.08-19.72 4.28-24.27 0-.03.01-.05.02-.07.02-.05.03-.1.04-.14.03-.11.05-.19.05-.19.26-.78.17-1.53-.15-2.15v.02ZM82.98 58.94c.9-1.03 1.79-2.04 2.67-3.02-5.76 7.58-15.3 19.26-28.81 33.14 9.2-10.18 18.47-20.73 26.14-30.12Zm-32.55 52.81-.03-.03c.11.02.19.04.2.04a.47.47 0 0 0-.17 0v-.01Zm6.9 6.42-.05-.04.03-.03c.02 0 .03.02.04.02 0 .02-.02.03-.03.05h.01Zm8.36-7.21 1.38-1.44c.01.01.02.03.03.05-.47.46-.94.93-1.42 1.39h.01Zm2.24-2.21c.26-.3.56-.65.87-1.02.01-.01.02-.03.04-.04 3.29-3.39 6.68-6.82 10.18-10.25.02-.02.05-.04.07-.06.86-.66 1.82-1.39 2.72-2.08-4.52 4.32-9.11 8.78-13.88 13.46v-.01Zm21.65-55.88c-1.86 2.42-3.9 5.56-5.63 8.07-5.46 7.91-23.04 27.28-23.43 27.65-2.71 2.62-10.88 10.46-16.09 15.37-.14.13-.25.24-.34.35a.794.794 0 0 1 .03-1.13c24.82-23.4 39.88-42.89 46-51.38-.13.33-.24.69-.55 1.09l.01-.02Zm16.51 7.1-.01.02c0-.02-.02-.07.01-.02Zm-.91-5.13Zm-5.89 9.45c-2.26-1.31-3.32-3.27-2.71-5.25l.19-.66c.08-.19.17-.38.28-.57.59-.98 1.49-1.85 2.52-2.36.05-.02.1-.03.15-.04a.795.795 0 0 1-.04-.43c.05-.31.25-.58.66-.58.67 0 2.75.62 3.54 1.3.24.19.47.4.68.63.3.35.74.92.96 1.33.13.06.23.62.38.91.14.46.2.93.18 1.4 0 .02 0 .02.01.03-.03.07 0 .37-.04.4-.1.72-.36 1.43-.75 2.05-.04.05-.07.11-.11.16 0 .01-.02.02-.03.04-.3.43-.65.83-1.08 1.13-1.26.89-2.73 1.16-4.2.79a6.33 6.33 0 0 1-.57-.25l-.02-.03Zm16.27-1.63c-.49 2.05-1.09 4.19-1.8 6.38-.03.08-.03.16-.03.23-.1.01-.19.05-.27.11-4.44 3.26-8.73 6.62-12.98 10.11 3.67-3.32 7.39-6.62 11.23-9.95a6.409 6.409 0 0 0 2.11-3.74l.56-3.37.03-.1c.25-.71 1.34-.4 1.17.33h-.02Z" style="fill:currentColor;fill-rule:nonzero" transform="translate(-26.41 -29.49)"/>`;
|
||||
|
||||
@@ -507,7 +507,6 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
customPaperColor: "#ffffff",
|
||||
alignment: "center",
|
||||
margin: "normal",
|
||||
exportDPI: 300,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1048,15 +1048,14 @@ 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",
|
||||
EXPORTDIALOG_PDF_FIT_4_OPTION: "Fit to 4-pages",
|
||||
EXPORTDIALOG_PDF_FIT_6_OPTION: "Fit to 6-pages",
|
||||
EXPORTDIALOG_PDF_FIT_8_OPTION: "Fit to 8-pages",
|
||||
EXPORTDIALOG_PDF_FIT_12_OPTION: "Fit to 12-pages",
|
||||
EXPORTDIALOG_PDF_FIT_16_OPTION: "Fit to 16-pages",
|
||||
EXPORTDIALOG_PDF_FIT_2_OPTION: "Fit to max 2-pages",
|
||||
EXPORTDIALOG_PDF_FIT_4_OPTION: "Fit to max 4-pages",
|
||||
EXPORTDIALOG_PDF_FIT_6_OPTION: "Fit to max 6-pages",
|
||||
EXPORTDIALOG_PDF_FIT_8_OPTION: "Fit to max 8-pages",
|
||||
EXPORTDIALOG_PDF_FIT_12_OPTION: "Fit to max 12-pages",
|
||||
EXPORTDIALOG_PDF_FIT_16_OPTION: "Fit to max 16-pages",
|
||||
EXPORTDIALOG_PDF_SCALE_OPTION: "Use image scale (may span multiple pages)",
|
||||
EXPORTDIALOG_PDF_PAPER_COLOR: "Paper Color",
|
||||
EXPORTDIALOG_PDF_PAPER_WHITE: "White",
|
||||
@@ -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"))
|
||||
|
||||
@@ -226,7 +226,7 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
},
|
||||
{
|
||||
field: "createPDF",
|
||||
code: "async createPDF({SVG: SVGSVGElement[], scale?: PDFExportScale, pageProps?: PDFPageProperties}): Promise<ArrayBuffer>",
|
||||
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" +
|
||||
"\n" +
|
||||
@@ -241,11 +241,10 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
"@property {number} [zoom=1] - The zoom level for the SVG. Used only if fitToPage is false. If the SVG does not fit the page, it will be tiled over multiple pages.\n" +
|
||||
"\n" +
|
||||
"@typedef {Object} PDFPageProperties\n" +
|
||||
"@property {{width: number, height: number}} [dimensions] - The dimensions of the PDF pages. Use getPageDimensions to get standard page sizes.\n" +
|
||||
"@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.\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" +
|
||||
"@property {number} exportDPI - The DPI of the exported PDF (150/300/600/1200).\n" +
|
||||
"\n" +
|
||||
"@example\n" +
|
||||
"const pdfData = await createPDF({\n" +
|
||||
@@ -256,22 +255,21 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
" backgroundColor: \"#ffffff\",\n" +
|
||||
" margin: { left: 20, right: 20, top: 20, bottom: 20 },\n" +
|
||||
" alignment: \"center\"\n" +
|
||||
" exportDPI: 300\n" +
|
||||
" }\n" +
|
||||
"});",
|
||||
},
|
||||
{
|
||||
field: "getPagePDFDimensions",
|
||||
code: "getPagePDFDimensions(pageSize: PageSize, orientation: PageOrientation): PageDimensions",
|
||||
desc: "Returns the dimensions of a standard page size in points (pt).\n" +
|
||||
desc: "Returns the dimensions of a standard page size in pixels.\n" +
|
||||
"\n" +
|
||||
"@param {PageSize} pageSize - The standard page size. Possible values are \"A0\", \"A1\", \"A2\", \"A3\", \"A4\", \"A5\", \"Letter\", \"Legal\", \"Tabloid\".\n" +
|
||||
"@param {PageOrientation} orientation - The orientation of the page. Possible values are \"portrait\" and \"landscape\".\n" +
|
||||
"@returns {PageDimensions} - An object containing the width and height of the page in points (pt).\n" +
|
||||
"@returns {PageDimensions} - An object containing the width and height of the page in pixels.\n" +
|
||||
"\n" +
|
||||
"@typedef {Object} PageDimensions\n" +
|
||||
"@property {number} width - The width of the page in points (pt).\n" +
|
||||
"@property {number} height - The height of the page in points (pt).\n" +
|
||||
"@property {number} width - The width of the page in pixels.\n" +
|
||||
"@property {number} height - The height of the page in pixels.\n" +
|
||||
"\n" +
|
||||
"@typedef {\"A0\" | \"A1\" | \"A2\" | \"A3\" | \"A4\" | \"A5\" | \"Letter\" | \"Legal\" | \"Tabloid\"} PageSize\n" +
|
||||
"\n" +
|
||||
|
||||
@@ -932,19 +932,19 @@ export class ExcalidrawAutomate {
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the dimensions of a standard page size in points (pt).
|
||||
* Returns the dimensions of a standard page size in pixels.
|
||||
*
|
||||
* @param {PageSize} pageSize - The standard page size. Possible values are "A0", "A1", "A2", "A3", "A4", "A5", "Letter", "Legal", "Tabloid".
|
||||
* @param {PageOrientation} orientation - The orientation of the page. Possible values are "portrait" and "landscape".
|
||||
* @returns {PageDimensions} - An object containing the width and height of the page in points (pt).
|
||||
* @returns {PageDimensions} - An object containing the width and height of the page in pixels.
|
||||
*
|
||||
* @typedef {Object} PageDimensions
|
||||
* @property {number} width - The width of the page in points (pt).
|
||||
* @property {number} height - The height of the page in points (pt).
|
||||
* @property {number} width - The width of the page in pixels.
|
||||
* @property {number} height - The height of the page in pixels.
|
||||
*
|
||||
* @example
|
||||
* const dimensions = getPageDimensions("A4", "portrait");
|
||||
* console.log(dimensions); // { width: 595.28, height: 841.89 }
|
||||
* console.log(dimensions); // { width: 794.56, height: 1122.56 }
|
||||
*/
|
||||
getPagePDFDimensions(pageSize: PageSize, orientation: PageOrientation): PageDimensions {
|
||||
return getPageDimensions(pageSize, orientation);
|
||||
@@ -964,11 +964,10 @@ export class ExcalidrawAutomate {
|
||||
* SVG: [svgElement1, svgElement2],
|
||||
* scale: { fitToPage: 1 },
|
||||
* pageProps: {
|
||||
* dimensions: { width: 595.28, height: 841.89 },
|
||||
* dimensions: { width: 794.56, height: 1122.56 },
|
||||
* backgroundColor: "#ffffff",
|
||||
* margin: { left: 20, right: 20, top: 20, bottom: 20 },
|
||||
* alignment: "center",
|
||||
* exportDPI: 300,
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
@@ -976,16 +975,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 +999,7 @@ export class ExcalidrawAutomate {
|
||||
pageProps.backgroundColor = "#ffffff";
|
||||
}
|
||||
|
||||
return await exportToPDF({SVG, scale, pageProps});
|
||||
await exportToPDF({SVG, scale, pageProps, filename});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
@@ -45,21 +33,24 @@ export interface PageDimensions {
|
||||
|
||||
export type PageOrientation = "portrait" | "landscape";
|
||||
|
||||
// All dimensions in points (pt)
|
||||
// All dimensions in pixels (pt)
|
||||
export const STANDARD_PAGE_SIZES = {
|
||||
"A0": { width: 2383.94, height: 3370.39 },
|
||||
"A1": { width: 1683.78, height: 2383.94 },
|
||||
"A2": { width: 1190.55, height: 1683.78 },
|
||||
"A3": { width: 841.89, height: 1190.55 },
|
||||
"A4": { width: 595.28, height: 841.89 },
|
||||
"A5": { width: 419.53, height: 595.28 },
|
||||
"Letter": { width: 612, height: 792 },
|
||||
"Legal": { width: 612, height: 1008 },
|
||||
"Tabloid": { width: 792, height: 1224 },
|
||||
A0: { width: 3179.52, height: 4494.96 }, // 33.11 × 46.81 inches
|
||||
A1: { width: 2245.76, height: 3179.52 }, // 23.39 × 33.11 inches
|
||||
A2: { width: 1587.76, height: 2245.76 }, // 16.54 × 23.39 inches
|
||||
A3: { width: 1122.56, height: 1587.76 }, // 11.69 × 16.54 inches
|
||||
A4: { width: 794.56, height: 1122.56 }, // 8.27 × 11.69 inches
|
||||
A5: { width: 559.37, height: 794.56 }, // 5.83 × 8.27 inches
|
||||
A6: { width: 397.28, height: 559.37 }, // 4.13 × 5.83 inches
|
||||
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
|
||||
} as const;
|
||||
|
||||
export type PageSize = keyof typeof STANDARD_PAGE_SIZES;
|
||||
|
||||
//margins are in pixels
|
||||
export function getMarginValue(margin:PDFPageMarginString): PDFMargin {
|
||||
switch(margin) {
|
||||
case "none": return { left: 0, right: 0, top: 0, bottom: 0 };
|
||||
@@ -76,15 +67,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 +321,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 +332,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 = 'absolute';
|
||||
clonedSVG.style.left = `${tile.x}px`;
|
||||
clonedSVG.style.top = `${tile.y + (i-1)*height}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