Compare commits

..

8 Commits

Author SHA1 Message Date
zsviczian
a796621f93 2.8.0-beta-3, import pdf-lib on demand, fixed area hover preview, fixed local font in popout windows, fixed scrollable elements panel and context menu on (not only mobile)
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
2025-01-19 20:11:19 +01:00
zsviczian
3c943c6685 Merge pull request #2220 from dmscode/master
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
Update zh-cn.ts to 01e3921
2025-01-19 18:50:27 +01:00
zsviczian
4209774b4e 2.8.0-beta-2 2025-01-19 15:56:39 +01:00
zsviczian
b18637f7d0 pdf export fitToPage fix number of pages
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
2025-01-19 08:35:01 +01:00
dmscode
af8a848d14 Update zh-cn.ts to 01e3921 2025-01-19 15:27:16 +08:00
zsviczian
01e392158d fix png, jpg conversion and move from symbol to inline
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
2025-01-18 22:42:49 +01:00
zsviczian
fc47b7aa0d 2.8.0-beta-1, 0.17.6-27 2025-01-18 19:12:57 +01:00
zsviczian
a0e0627a49 Merge pull request #2219 from zsviczian/PDF-export
Pdf export
2025-01-18 18:59:50 +01:00
15 changed files with 413 additions and 252 deletions

View File

@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "2.7.6-beta-1",
"version": "2.8.0-beta-3",
"minAppVersion": "1.1.6",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",

169
package-lock.json generated
View File

@@ -12,7 +12,7 @@
"@cantoo/pdf-lib": "^2.2.4",
"@popperjs/core": "^2.11.8",
"@zsviczian/colormaster": "^1.2.2",
"@zsviczian/excalidraw": "0.17.6-26",
"@zsviczian/excalidraw": "0.17.6-27",
"chroma-js": "^2.4.2",
"clsx": "^2.0.0",
"es6-promise-pool": "2.5.0",
@@ -79,7 +79,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
"devOptional": true,
"dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
@@ -92,7 +92,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
"integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/highlight": "^7.24.7",
"picocolors": "^1.0.0"
@@ -105,7 +105,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.8.tgz",
"integrity": "sha512-c4IM7OTg6k1Q+AJ153e2mc2QVTezTwnb4VzquwcyiEzGnW0Kedv4do/TrkU98qPeC5LNiMt/QXwIjzYXLBpyZg==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -114,7 +114,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.8.tgz",
"integrity": "sha512-6AWcmZC/MZCO0yKys4uhg5NlxL0ESF3K6IAaoQ+xSXvPyPyxNWRafP+GDbI88Oh68O7QkJgmEtedWPM9U0pZNg==",
"devOptional": true,
"dev": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.24.7",
@@ -144,7 +144,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.8.tgz",
"integrity": "sha512-47DG+6F5SzOi0uEvK4wMShmn5yY0mVjVJoWTphdY2B4Rx9wHgjK7Yhtr0ru6nE+sn0v38mzrWOlah0p/YlHHOQ==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/types": "^7.24.8",
"@jridgewell/gen-mapping": "^0.3.5",
@@ -159,7 +159,7 @@
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
@@ -197,7 +197,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz",
"integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/compat-data": "^7.24.8",
"@babel/helper-validator-option": "^7.24.8",
@@ -269,7 +269,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz",
"integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/types": "^7.24.7"
},
@@ -281,7 +281,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz",
"integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/template": "^7.24.7",
"@babel/types": "^7.24.7"
@@ -294,7 +294,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz",
"integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/types": "^7.24.7"
},
@@ -319,7 +319,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
"integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/traverse": "^7.24.7",
"@babel/types": "^7.24.7"
@@ -332,7 +332,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.8.tgz",
"integrity": "sha512-m4vWKVqvkVAWLXfHCCfff2luJj86U+J0/x+0N3ArG/tP0Fq7zky2dYwMbtPmkc/oulkkbjdL3uWzuoBwQ8R00Q==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/helper-environment-visitor": "^7.24.7",
"@babel/helper-module-imports": "^7.24.7",
@@ -406,7 +406,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz",
"integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/traverse": "^7.24.7",
"@babel/types": "^7.24.7"
@@ -432,7 +432,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
"integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/types": "^7.24.7"
},
@@ -444,7 +444,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
"integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -453,7 +453,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
"integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -462,7 +462,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz",
"integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -486,7 +486,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz",
"integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/template": "^7.24.7",
"@babel/types": "^7.24.8"
@@ -499,7 +499,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
"integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.24.7",
"chalk": "^2.4.2",
@@ -514,7 +514,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz",
"integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==",
"devOptional": true,
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -1834,7 +1834,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz",
"integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.24.7",
"@babel/parser": "^7.24.7",
@@ -1848,7 +1848,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz",
"integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.24.7",
"@babel/generator": "^7.24.8",
@@ -1869,7 +1869,7 @@
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.8.tgz",
"integrity": "sha512-SkSBEHwwJRU52QEVZBmMBnE5Ux2/6WU1grdYyOhpbCNxbmJrDuDCphBzKZSO3taf0zztp+qkWlymE5tVL5l0TA==",
"devOptional": true,
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.24.8",
"@babel/helper-validator-identifier": "^7.24.7",
@@ -2248,7 +2248,7 @@
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"devOptional": true,
"dev": true,
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@@ -2262,7 +2262,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=6.0.0"
}
@@ -2271,7 +2271,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=6.0.0"
}
@@ -2290,13 +2290,13 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"devOptional": true
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"devOptional": true,
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
@@ -3366,9 +3366,9 @@
"license": "MIT"
},
"node_modules/@zsviczian/excalidraw": {
"version": "0.17.6-26",
"resolved": "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.17.6-26.tgz",
"integrity": "sha512-UAqr7b7cxIbOvK1u0NKqgAs0wB9KYUsVc6Q2J+yviM4ae+wkTXp8qW/V4mdWADJ3lTG5RgXCb9ausIKfSkPNRg==",
"version": "0.17.6-27",
"resolved": "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.17.6-27.tgz",
"integrity": "sha512-8E5pfMbKO80d9oXyApF43SCKaR0CcLhVHjiQYEuAXzS7v6XzrDHODsNg+HuN9kOiShXmmn/e1MKVawo7bmeuOQ==",
"dependencies": {
"@braintree/sanitize-url": "6.0.2",
"@excalidraw/random-username": "1.1.0",
@@ -3384,7 +3384,8 @@
"fractional-indexing": "3.2.0",
"fuzzy": "0.1.3",
"image-blob-reduce": "3.0.1",
"jotai": "1.13.1",
"jotai": "2.11.0",
"jotai-scope": "0.7.2",
"lodash.throttle": "4.1.1",
"nanoid": "3.3.3",
"open-color": "1.9.1",
@@ -3540,7 +3541,7 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"devOptional": true,
"dev": true,
"dependencies": {
"color-convert": "^1.9.0"
},
@@ -3683,7 +3684,7 @@
"version": "4.23.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz",
"integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==",
"devOptional": true,
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -3755,7 +3756,7 @@
"version": "1.0.30001641",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001641.tgz",
"integrity": "sha512-Phv5thgl67bHYo1TtMY/MurjkHhV4EDaCosezRXgZ8jzA/Ub+wjxAvbGvjoFENStinwi5kCyOYV3mi5tOGykwA==",
"devOptional": true,
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -3780,7 +3781,7 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"devOptional": true,
"dev": true,
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@@ -3863,7 +3864,7 @@
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"devOptional": true,
"dev": true,
"dependencies": {
"color-name": "1.1.3"
}
@@ -3934,7 +3935,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"devOptional": true
"dev": true
},
"node_modules/core-js-compat": {
"version": "3.37.1",
@@ -4868,7 +4869,7 @@
"version": "1.4.827",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.827.tgz",
"integrity": "sha512-VY+J0e4SFcNfQy19MEoMdaIcZLmDCprqvBtkii1WTCTQHpRvf5N8+3kTYCgL/PcntvwQvmMJWTuDPsq+IlhWKQ==",
"devOptional": true
"dev": true
},
"node_modules/elkjs": {
"version": "0.9.3",
@@ -4908,7 +4909,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=6"
}
@@ -4917,7 +4918,7 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=0.8.0"
}
@@ -5467,7 +5468,7 @@
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -5523,7 +5524,7 @@
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=4"
}
@@ -5576,7 +5577,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=4"
}
@@ -5876,62 +5877,34 @@
}
},
"node_modules/jotai": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/jotai/-/jotai-1.13.1.tgz",
"integrity": "sha512-RUmH1S4vLsG3V6fbGlKzGJnLrDcC/HNb5gH2AeA9DzuJknoVxSGvvg8OBB7lke+gDc4oXmdVsaKn/xDUhWZ0vw==",
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.11.0.tgz",
"integrity": "sha512-zKfoBBD1uDw3rljwHkt0fWuja1B76R7CjznuBO+mSX6jpsO1EBeWNRKpeaQho9yPI/pvCv4recGfgOXGxwPZvQ==",
"engines": {
"node": ">=12.20.0"
},
"peerDependencies": {
"@babel/core": "*",
"@babel/template": "*",
"jotai-devtools": "*",
"jotai-immer": "*",
"jotai-optics": "*",
"jotai-redux": "*",
"jotai-tanstack-query": "*",
"jotai-urql": "*",
"jotai-valtio": "*",
"jotai-xstate": "*",
"jotai-zustand": "*",
"react": ">=16.8"
"@types/react": ">=17.0.0",
"react": ">=17.0.0"
},
"peerDependenciesMeta": {
"@babel/core": {
"@types/react": {
"optional": true
},
"@babel/template": {
"optional": true
},
"jotai-devtools": {
"optional": true
},
"jotai-immer": {
"optional": true
},
"jotai-optics": {
"optional": true
},
"jotai-redux": {
"optional": true
},
"jotai-tanstack-query": {
"optional": true
},
"jotai-urql": {
"optional": true
},
"jotai-valtio": {
"optional": true
},
"jotai-xstate": {
"optional": true
},
"jotai-zustand": {
"react": {
"optional": true
}
}
},
"node_modules/jotai-scope": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/jotai-scope/-/jotai-scope-0.7.2.tgz",
"integrity": "sha512-Gwed97f3dDObrO43++2lRcgOqw4O2sdr4JCjP/7eHK1oPACDJ7xKHGScpJX9XaflU+KBHXF+VhwECnzcaQiShg==",
"peerDependencies": {
"jotai": ">=2.9.2",
"react": ">=17.0.0"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -5986,7 +5959,7 @@
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"devOptional": true,
"dev": true,
"bin": {
"json5": "lib/cli.js"
},
@@ -6155,7 +6128,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"devOptional": true,
"dev": true,
"dependencies": {
"yallist": "^3.0.2"
}
@@ -6869,7 +6842,7 @@
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
"devOptional": true
"dev": true
},
"node_modules/non-layered-tidy-tree-layout": {
"version": "2.0.2",
@@ -7125,7 +7098,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
"devOptional": true
"dev": true
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -8391,7 +8364,7 @@
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"devOptional": true,
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
@@ -8653,7 +8626,7 @@
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"devOptional": true,
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
@@ -8764,7 +8737,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=4"
}
@@ -9001,7 +8974,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
"integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
"devOptional": true,
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -9362,7 +9335,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"devOptional": true
"dev": true
},
"node_modules/yn": {
"version": "3.1.1",

View File

@@ -24,7 +24,7 @@
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.11.8",
"@zsviczian/excalidraw": "0.17.6-26",
"@zsviczian/excalidraw": "0.17.6-27",
"chroma-js": "^2.4.2",
"clsx": "^2.0.0",
"@zsviczian/colormaster": "^1.2.2",

View File

@@ -147,6 +147,7 @@ const BUILD_CONFIG = {
entryFileNames: 'main.js',
format: 'cjs',
exports: 'default',
inlineDynamicImports: true, // Add this line only
},
plugins: getRollupPlugins(
{

View File

@@ -801,15 +801,16 @@ const tmpObsidianWYSIWYG = async (
if(Boolean(ctx.frontmatter)) {
el.empty();
} else {
const warningEl = el.querySelector("div>h3[data-heading^='Unable to find section #^");
//Obsidian changed this at some point from h3 to h5 and also the text...
const warningEl = el.querySelector("div>*[data-heading^='Unable to find ");
if(warningEl) {
const ref = warningEl.getAttr("data-heading").match(/Unable to find section (#\^(?:group=|area=|frame=|clippedframe=)[^ ]*)/)?.[1];
const dataHeading = warningEl.getAttr("data-heading");
const ref = warningEl.getAttr("data-heading").match(/Unable to find[^^]+(\^(?:group=|area=|frame=|clippedframe=)[^ ”]+)/)?.[1];
if(ref) {
attr.fname = file.path + ref;
attr.fname = file.path + "#" +ref;
areaPreview = true;
}
}
}
if(!isFrontmatterDiv && !areaPreview) {
if(el.parentElement === containerEl) containerEl.removeChild(el);

View File

@@ -502,11 +502,12 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
pdfSettings: {
pageSize: "A4",
pageOrientation: "portrait",
fitToPage: true,
fitToPage: 1,
paperColor: "white",
customPaperColor: "#ffffff",
alignment: "center",
margin: "normal"
margin: "normal",
exportDPI: 300,
},
};
@@ -2508,6 +2509,9 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
this.requestReloadDrawings = true;
this.plugin.settings.experimentalEnableFourthFont = value;
this.applySettingsUpdate();
if(value) {
this.plugin.initializeFonts();
}
}),
);

View File

@@ -1048,8 +1048,15 @@ 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_SCALE_OPTION: "Use image scale (may span multiple pages)",
EXPORTDIALOG_PDF_PAPER_COLOR: "Paper Color",
EXPORTDIALOG_PDF_PAPER_WHITE: "White",
@@ -1079,4 +1086,8 @@ FILENAME_HEAD: "Filename",
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_DONE: "Export complete",
};

View File

@@ -386,13 +386,14 @@ FILENAME_HEAD: "文件名",
"此设置不会影响您在 Excalidraw 模式下的绘图显示,或者在将绘图嵌入 Markdown 文档时,或在渲染悬停预览时。<br><ul>" +
"<li>请参阅下面‘嵌入和导出’部分的 <a href='#"+TAG_PDFEXPORT+"'>PDF 导出</a> 相关设置。</li></ul><br>" +
"您必须关闭当前的 Excalidraw/Markdown 文件并重新打开,以使此更改生效。",
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_NAME: "在将 Excalidraw 文件导出为 PDF 时将文件渲染为图像",
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_NAME : "在 Obsidian 中导出为 PDF 格式时将 Excalidraw 渲染为图像" ,
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_DESC:
"处于 Markdown 视图模式时,此设置控制 Excalidraw 在使用 Obsidian 的 <b>导出为 PDF</b> 功能,将 Excalidraw 文件导出为 PDF 的行为。<br>" +
"<ul><li><b>启用</b> 时,PDF 将仅显示 Excalidraw 绘图</li>" +
"<li><b>禁用</b> 时,PDF 将显示文档的 Markdown 部分(背景笔记)。</li></ul>" +
"请参阅上面‘外观和行为’部分的 <<a href='#"+TAG_MDREADINGMODE+"'>>Markdown 阅读模式</a> 相关设置。" +
"⚠️ 注意,您必须关闭当前的 Excalidraw/Markdown 文件并重新打开,以使此更改生效。⚠️",
"此设置控制在使用 Obsidian 内置的<b>导出为 PDF</b>功能,如何将 Excalidraw 文件导出为 PDF。<br>" +
"<ul><li><b>启用</b>PDF 将包含图像格式的 Excalidraw 绘图</li>" +
"<li><b>禁用</b>PDF 将包含作为文本的 Markdown 内容。</li></ul>" +
"注意:此设置不会影响 Excalidraw 本身的 PDF 导出功能。<br>" +
"请参阅上方“外观和行为”部分中与<a href='#" + TAG_MDREADINGMODE + "'>Markdown 阅读模式</a>相关的其他设置。<br>" +
"⚠️ 您必须关闭并重新打开 Excalidraw/Markdown 文件,设置更改才会生效。⚠️",
HOTKEY_OVERRIDE_HEAD: "热键覆盖",
HOTKEY_OVERRIDE_DESC: `一些 Excalidraw 的热键,例如 ${labelCTRL()}+Enter 用于编辑文本,或 ${labelCTRL()}+K 用于创建元素链接。` +
"与 Obsidian 的热键设置发生冲突。您在下面添加的热键组合将在使用 Excalidraw 时覆盖 Obsidian 的热键设置," +
@@ -661,6 +662,7 @@ FILENAME_HEAD: "文件名",
EXPORT_EMBED_SCENE_DESC:
"在导出的图像中嵌入 Excalidraw 场景。可以通过在文件级别添加 <code>excalidraw-export-embed-scene: true/false</code> frontmatter 元数据键来覆盖此设置。" +
"此设置仅在您下次(重新)打开绘图时生效。",
PDF_EXPORT_SETTINGS : "PDF 导出设置",
EXPORT_HEAD: "导出设置",
EXPORT_SYNC_NAME:
"保持 SVG/PNG 文件名与绘图文件同步",
@@ -1006,4 +1008,75 @@ FILENAME_HEAD: "文件名",
LINK_CLICK_POPOUT : "在弹出窗口中打开" ,
LINK_CLICK_NEW_TAB : "在新标签页中打开" ,
LINK_CLICK_MD_PROPS : "显示 Markdown 图片属性对话框(仅在嵌入 Markdown 文档为图片时适用)" ,
// 导出对话框
// 对话框和标签页
EXPORTDIALOG_TITLE : "导出图形",
EXPORTDIALOG_TAB_IMAGE : "图像",
EXPORTDIALOG_TAB_PDF : "PDF",
// 设置持久化
EXPORTDIALOG_SAVE_SETTINGS : "将图像设置保存到文件 doc.properties 吗?",
EXPORTDIALOG_SAVE_SETTINGS_SAVE : "保存为预设",
EXPORTDIALOG_SAVE_SETTINGS_ONETIME : "仅本次使用",
// 图像设置
EXPORTDIALOG_IMAGE_SETTINGS : "图像",
EXPORTDIALOG_IMAGE_DESC : "PNG 支持透明。外部文件可以包含 Excalidraw 场景数据。",
EXPORTDIALOG_PADDING : "边距",
EXPORTDIALOG_SCALE : "缩放",
EXPORTDIALOG_CURRENT_PADDING : "当前边距:",
EXPORTDIALOG_SIZE_DESC : "缩放会影响输出大小",
EXPORTDIALOG_SCALE_VALUE : "缩放:",
EXPORTDIALOG_IMAGE_SIZE : "大小:",
// 主题和背景
EXPORTDIALOG_EXPORT_THEME : "主题",
EXPORTDIALOG_THEME_LIGHT : "浅色",
EXPORTDIALOG_THEME_DARK : "深色",
EXPORTDIALOG_BACKGROUND : "背景",
EXPORTDIALOG_BACKGROUND_TRANSPARENT : "透明",
EXPORTDIALOG_BACKGROUND_USE_COLOR : "使用场景颜色",
// 选择
EXPORTDIALOG_SELECTED_ELEMENTS : "导出",
EXPORTDIALOG_SELECTED_ALL : "整个场景",
EXPORTDIALOG_SELECTED_SELECTED : "仅选中部分",
// 导出选项
EXPORTDIALOG_EMBED_SCENE : "包含场景数据吗?",
EXPORTDIALOG_EMBED_YES : "是",
EXPORTDIALOG_EMBED_NO : "否",
// PDF 设置
EXPORTDIALOG_PDF_SETTINGS : "PDF",
EXPORTDIALOG_PAGE_SIZE : "页面大小",
EXPORTDIALOG_PAGE_ORIENTATION : "方向",
EXPORTDIALOG_ORIENTATION_PORTRAIT : "纵向",
EXPORTDIALOG_ORIENTATION_LANDSCAPE : "横向",
EXPORTDIALOG_PDF_FIT_TO_PAGE : "页面适配",
EXPORTDIALOG_PDF_FIT_OPTION : "适配页面",
EXPORTDIALOG_PDF_SCALE_OPTION : "使用图像缩放(可能跨多页)",
EXPORTDIALOG_PDF_PAPER_COLOR : "纸张颜色",
EXPORTDIALOG_PDF_PAPER_WHITE : "白色",
EXPORTDIALOG_PDF_PAPER_SCENE : "使用场景颜色",
EXPORTDIALOG_PDF_PAPER_CUSTOM : "自定义颜色",
EXPORTDIALOG_PDF_ALIGNMENT : "页面位置",
EXPORTDIALOG_PDF_ALIGN_CENTER : "居中",
EXPORTDIALOG_PDF_ALIGN_TOP_LEFT : "左上角",
EXPORTDIALOG_PDF_ALIGN_TOP_CENTER : "顶部居中",
EXPORTDIALOG_PDF_ALIGN_TOP_RIGHT : "右上角",
EXPORTDIALOG_PDF_ALIGN_BOTTOM_LEFT : "左下角",
EXPORTDIALOG_PDF_ALIGN_BOTTOM_CENTER : "底部居中",
EXPORTDIALOG_PDF_ALIGN_BOTTOM_RIGHT : "右下角",
EXPORTDIALOG_PDF_MARGIN : "边距",
EXPORTDIALOG_PDF_MARGIN_NONE : "无",
EXPORTDIALOG_PDF_MARGIN_TINY : "小",
EXPORTDIALOG_PDF_MARGIN_NORMAL : "正常",
EXPORTDIALOG_SAVE_PDF_SETTINGS : "保存 PDF 设置",
EXPORTDIALOG_SAVE_CONFIRMATION : "PDF 配置已保存为插件默认设置",
// 按钮
EXPORTDIALOG_PNGTOFILE : "导出 PNG 文件",
EXPORTDIALOG_SVGTOFILE : "导出 SVG 文件",
EXPORTDIALOG_PNGTOVAULT : "PNG 保存到 Vault",
EXPORTDIALOG_SVGTOVAULT : "SVG 保存到 Vault",
EXPORTDIALOG_EXCALIDRAW : "Excalidraw",
EXPORTDIALOG_PNGTOCLIPBOARD : "PNG 复制到剪贴板",
EXPORTDIALOG_SVGTOCLIPBOARD : "SVG 复制到剪贴板",
EXPORTDIALOG_PDF : "导出 PDF 文件",
EXPORTDIALOG_PDFTOVAULT : "PDF 保存到 Vault",
};

View File

@@ -38,11 +38,12 @@ export class ExportDialog extends Modal {
private contentContainer: HTMLDivElement;
private buttonContainerRow1: HTMLDivElement;
private buttonContainerRow2: HTMLDivElement;
public fitToPage: boolean = true;
public fitToPage: number = 1;
public paperColor: "white" | "scene" | "custom" = "white";
public customPaperColor: string = "#ffffff";
public alignment: PDFPageAlignment = "center";
public margin: PDFPageMarginString = "normal";
public exportDPI: number = 300;
constructor(
private plugin: ExcalidrawPlugin,
@@ -68,6 +69,7 @@ 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;
}
@@ -276,7 +278,8 @@ export class ExportDialog extends Modal {
paperColor: this.paperColor,
customPaperColor: this.customPaperColor,
alignment: this.alignment,
margin: this.margin
margin: this.margin,
exportDPI: this.exportDPI,
};
new PDFExportSettingsComponent(
@@ -290,6 +293,7 @@ export class ExportDialog extends Modal {
this.customPaperColor = pdfSettings.customPaperColor;
this.alignment = pdfSettings.alignment;
this.margin = pdfSettings.margin;
this.exportDPI = pdfSettings.exportDPI ?? 300;
}
).render();
}
@@ -378,7 +382,8 @@ export class ExportDialog extends Modal {
paperColor: this.paperColor,
customPaperColor: this.customPaperColor,
alignment: this.alignment,
margin: this.margin
margin: this.margin,
exportDPI: this.exportDPI,
};
await this.plugin.saveSettings();
new Notice(t("EXPORTDIALOG_SAVE_CONFIRMATION"));

View File

@@ -5,11 +5,12 @@ import { t } from "src/lang/helpers";
export interface PDFExportSettings {
pageSize: PageSize;
pageOrientation: PageOrientation;
fitToPage: boolean;
fitToPage: number;
paperColor: "white" | "scene" | "custom";
customPaperColor: string;
alignment: PDFPageAlignment;
margin: PDFPageMarginString;
exportDPI: number;
}
export class PDFExportSettingsComponent {
@@ -55,17 +56,42 @@ export class PDFExportSettingsComponent {
})
);
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"))
.addDropdown(dropdown =>
dropdown
.addOptions({
"scale": t("EXPORTDIALOG_PDF_SCALE_OPTION"),
"fit": t("EXPORTDIALOG_PDF_FIT_OPTION"),
"scale": t("EXPORTDIALOG_PDF_SCALE_OPTION")
"fit-2": t("EXPORTDIALOG_PDF_FIT_2_OPTION"),
"fit-4": t("EXPORTDIALOG_PDF_FIT_4_OPTION"),
"fit-6": t("EXPORTDIALOG_PDF_FIT_6_OPTION"),
"fit-8": t("EXPORTDIALOG_PDF_FIT_8_OPTION"),
"fit-12": t("EXPORTDIALOG_PDF_FIT_12_OPTION"),
"fit-16": t("EXPORTDIALOG_PDF_FIT_16_OPTION")
})
.setValue(this.settings.fitToPage ? "fit" : "scale")
.setValue(this.settings.fitToPage === 1 ? "fit" :
(typeof this.settings.fitToPage === "number" ? `fit-${this.settings.fitToPage}` : "scale"))
.onChange(value => {
this.settings.fitToPage = value === "fit";
this.settings.fitToPage = value === "scale" ? 0 :
(value === "fit" ? 1 : parseInt(value.split("-")[1]));
this.update();
})
);

View File

@@ -245,6 +245,7 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
"@property {string} [backgroundColor] - The background color of the PDF pages.\n" +
"@property {PDFMargin} margin - The margins of the PDF pages.\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" +
@@ -255,6 +256,7 @@ 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" +
"});",
},

View File

@@ -955,25 +955,26 @@ export class ExcalidrawAutomate {
*
* @param {Object} params - The parameters for creating the PDF.
* @param {SVGSVGElement[]} params.SVG - An array of SVG elements to be included in the PDF.
* @param {PDFExportScale} [params.scale={ fitToPage: true, zoom: 1 }] - The scaling options for the SVG elements.
* @param {PDFExportScale} [params.scale={ fitToPage: 1, zoom: 1 }] - The scaling options for the SVG elements.
* @param {PDFPageProperties} [params.pageProps] - The properties for the PDF pages.
* @returns {Promise<ArrayBuffer>} - A promise that resolves to an ArrayBuffer containing the PDF data.
*
* @example
* const pdfData = await createToPDF({
* SVG: [svgElement1, svgElement2],
* scale: { fitToPage: true },
* scale: { fitToPage: 1 },
* pageProps: {
* dimensions: { width: 595.28, height: 841.89 },
* backgroundColor: "#ffffff",
* margin: { left: 20, right: 20, top: 20, bottom: 20 },
* alignment: "center"
* alignment: "center",
* exportDPI: 300,
* }
* });
*/
async createPDF({
SVG,
scale = { fitToPage: true, zoom: 1 },
scale = { fitToPage: 1, zoom: 1 },
pageProps,
}: {
SVG: SVGSVGElement[];
@@ -984,6 +985,7 @@ export class ExcalidrawAutomate {
pageProps = {
alignment: this.plugin.settings.pdfSettings.alignment,
margin: getMarginValue(this.plugin.settings.pdfSettings.margin),
exportDPI: this.plugin.settings.pdfSettings.exportDPI ?? 300,
};
}

View File

@@ -1,12 +1,25 @@
import { PDFDocument, rgb } from '@cantoo/pdf-lib';
import { Notice } from 'obsidian';
import { getEA } from 'src/core';
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;
export type PDFPageAlignment = "center" | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
export type PDFPageMarginString = "none" | "tiny" | "normal";
export interface PDFExportScale {
fitToPage: boolean;
fitToPage: number; // 0 means use zoom, >1 means fit to that many pages exactly
zoom?: number;
}
@@ -22,6 +35,7 @@ export interface PDFPageProperties {
backgroundColor?: string;
margin: PDFMargin;
alignment: PDFPageAlignment;
exportDPI: number;
}
export interface PageDimensions {
@@ -80,28 +94,10 @@ function calculatePosition(
pageHeight: number,
margin: PDFMargin,
alignment: PDFPageAlignment,
scale: PDFExportScale
): {x: number, y: number} {
const availableWidth = pageWidth - margin.left - margin.right;
const availableHeight = pageHeight - margin.top - margin.bottom;
console.log(JSON.stringify({
message: 'PDF Position Debug',
input: {
svgWidth,
svgHeight,
pageWidth,
pageHeight,
margin,
alignment,
scale
},
calculated: {
availableWidth,
availableHeight
}
}));
let x = margin.left;
let y = margin.bottom;
@@ -121,52 +117,70 @@ function calculatePosition(
y = pageHeight - margin.top - svgHeight;
}
console.log(JSON.stringify({
message: 'PDF Position Intermediate',
x,
y,
alignment,
availableHeight,
marginTop: margin.top,
marginBottom: margin.bottom,
svgHeight,
pageHeight
}));
console.log(JSON.stringify({
message: 'PDF Position Result',
x,
y,
finalPosition: {
bottom: y,
top: y + svgHeight,
left: x,
right: x + svgWidth
}
}));
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
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;
let finalWidth: number;
let finalHeight: number;
// 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;
if (scale.fitToPage) {
const ratio = Math.min(availableWidth / svgWidth, availableHeight / svgHeight);
finalWidth = svgWidth * ratio;
finalHeight = svgHeight * ratio;
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,
@@ -174,9 +188,8 @@ function calculateDimensions(
pageDim.height,
margin,
alignment,
scale
);
return [{
width: finalWidth,
height: finalHeight,
@@ -184,71 +197,52 @@ function calculateDimensions(
y: position.y
}];
} else {
// Scale mode - may need multiple pages
finalWidth = svgWidth * (scale.zoom || 1);
finalHeight = svgHeight * (scale.zoom || 1);
// 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);
if (finalWidth <= availableWidth && finalHeight <= availableHeight) {
// Content fits on one page
const position = calculatePosition(
finalWidth,
finalHeight,
pageDim.width,
pageDim.height,
margin,
alignment,
scale
);
return [{
width: finalWidth,
height: finalHeight,
x: position.x,
y: position.y
}];
} else {
// Content needs to be tiled across multiple pages
const dimensions: SVGDimensions[] = [];
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 tileWidth = Math.min(availableWidth, finalWidth - col * availableWidth);
const tileHeight = Math.min(availableHeight, finalHeight - row * availableHeight);
// Calculate y coordinate following the same logic as single-page rendering
// We start from the bottom margin and work our way up
//const y = margin.bottom + row * availableHeight;
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 * availableWidth / (scale.zoom || 1),
sourceY: row * availableHeight / (scale.zoom || 1),
sourceWidth: tileWidth / (scale.zoom || 1),
sourceHeight: tileHeight / (scale.zoom || 1)
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;
}
return dimensions;
}
}
async function addSVGToPage(
pdfDoc: PDFDocument,
pdfDoc: Awaited<ReturnType<typeof import('@cantoo/pdf-lib').PDFDocument.create>>,
svg: SVGSVGElement,
dimensions: SVGDimensions,
pageDim: PageDimensions,
backgroundColor?: string
pageProps: PDFPageProperties
) {
const { rgb } = await getPDFLib();
const page = pdfDoc.addPage([pageDim.width, pageDim.height]);
if (backgroundColor && backgroundColor !== '#ffffff') {
const { r, g, b } = hexToRGB(backgroundColor);
if (pageProps.backgroundColor && pageProps.backgroundColor !== '#ffffff') {
const { r, g, b } = hexToRGB(pageProps.backgroundColor);
page.drawRectangle({
x: 0,
y: 0,
@@ -258,52 +252,90 @@ async function addSVGToPage(
});
}
// Clone and modify SVG for tiling if needed
let svgToEmbed = svg;
if (dimensions.sourceX !== undefined) {
svgToEmbed = svg.cloneNode(true) as SVGSVGElement;
const viewBox = `${dimensions.sourceX} ${dimensions.sourceY} ${dimensions.sourceWidth} ${dimensions.sourceHeight}`;
svgToEmbed.setAttribute('viewBox', viewBox);
svgToEmbed.setAttribute('width', String(dimensions.sourceWidth));
svgToEmbed.setAttribute('height', String(dimensions.sourceHeight));
}
const svgImage = await pdfDoc.embedSvg(svgToEmbed.outerHTML);
// 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);
console.log(JSON.stringify({message: "addSVGToPage", dimensions, html: svgToEmbed.outerHTML}));
// Adjust y-coordinate to account for PDF coordinate system
const adjustedY = pageDim.height - dimensions.y;
const adjustedY = pageDim.height - dimensions.y - dimensions.height;
page.drawSvg(svgImage, {
// Draw the image
page.drawImage(image, {
x: dimensions.x,
y: adjustedY,
width: dimensions.width,
height: dimensions.height,
});
console.log(JSON.stringify({
message: 'PDF Draw SVG',
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: true, zoom: 1 },
scale = { fitToPage: 1, zoom: 1 },
pageProps,
}: {
SVG: SVGSVGElement[];
scale: PDFExportScale;
pageProps: PDFPageProperties;
}): Promise<ArrayBuffer> {
const { PDFDocument } = await getPDFLib();
const pdfDoc = await PDFDocument.create();
const msg = t('EXPORTDIALOG_PDF_PROGRESS_NOTICE');
const imgmsg = t('EXPORTDIALOG_PDF_PROGRESS_IMAGE');
let notice = new Notice(msg, 0);
//@ts-ignore
let noticeContainerEl = notice.containerEl ?? notice.noticeEl;
let j=1;
for (const svg of SVG) {
const svgWidth = parseFloat(svg.getAttribute('width') || '0');
const svgHeight = parseFloat(svg.getAttribute('height') || '0');
@@ -314,14 +346,31 @@ export async function exportToPDF({
pageProps.dimensions,
pageProps.margin,
scale,
pageProps.alignment
pageProps.alignment,
pageProps.exportDPI
);
for (const dim of dimensions) {
await addSVGToPage(pdfDoc, svg, dim, pageProps.dimensions, pageProps.backgroundColor);
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);
}
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'));
}
return pdfDoc.save();
}

View File

@@ -589,16 +589,16 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
const pdfArrayBuffer = await exportToPDF({
SVG: [svg],
scale: {
...this.exportDialog.fitToPage
? { fitToPage: true }
: { zoom: this.exportDialog.scale, fitToPage: false },
scale: {
zoom: this.exportDialog.scale,
fitToPage: this.exportDialog.fitToPage
},
pageProps: {
dimensions: getPageDimensions(pageSize, orientation),
backgroundColor: this.exportDialog.getPaperColor(),
margin: getMarginValue(this.exportDialog.margin),
alignment: this.exportDialog.alignment,
exportDPI: this.exportDialog.exportDPI,
}
});
@@ -1578,6 +1578,9 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
this.packages = this.plugin.getPackage(this.ownerWindow);
if(DEVICE.isDesktop && !apiMissing) {
if(this.ownerWindow !== window) {
this.plugin.initializeFonts();
}
this.destroyers.push(
//this.containerEl.onWindowMigrated(this.leaf.rebuildView.bind(this))
this.containerEl.onWindowMigrated(async() => {

View File

@@ -116,6 +116,7 @@ li[data-testid] {
border: 0 !important;
box-shadow: 0 !important;
background-color: transparent !important;
overflow-y: auto !important;
}
.excalidraw .popover {
@@ -336,6 +337,16 @@ label.color-input-container > input {
display: none !important;
}
.excalidraw .App-toolbar-content .dropdown-menu {
max-height: 70vh;
overflow-y: auto;
}
.excalidraw .panelColumn {
max-height: 70vh;
overflow-y: auto;
}
.excalidraw .panelColumn .buttonList {
max-width: 13rem;
}