From 579704aec10ca934d4c6cad066fc27b3d751b329 Mon Sep 17 00:00:00 2001 From: Lukas Lohoff Date: Fri, 24 Jun 2022 15:45:31 +0200 Subject: [PATCH] feat: pdf print --- assets/legend1.png | Bin 0 -> 4496 bytes assets/legend2.png | Bin 0 -> 4275 bytes package-lock.json | 271 +++++++++++++++++- package.json | 1 + src/Button/PrintButton/InkmapTypes.ts | 10 + src/Button/PrintButton/PrintButton.example.md | 21 +- src/Button/PrintButton/PrintButton.tsx | 126 +++++++- 7 files changed, 409 insertions(+), 20 deletions(-) create mode 100644 assets/legend1.png create mode 100644 assets/legend2.png diff --git a/assets/legend1.png b/assets/legend1.png new file mode 100644 index 0000000000000000000000000000000000000000..deba928f1dcd1de22546133ca0b69d74f62e297d GIT binary patch literal 4496 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 bo8YGgJA2iu;iHlt4N_;FE;v%{gVO#1%Ti{I literal 0 HcmV?d00001 diff --git a/package-lock.json b/package-lock.json index 80a0d3f934..4ebb24d471 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "@types/react": "^17.0", "@types/react-dom": "^18.0.2", "@types/react-rnd": "^8.0.0", + "jspdf": "^2.5.1", "lodash": "^4.17.21", "moment": "^2.29.2", "proj4": "^2.7.5", @@ -6432,6 +6433,12 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" }, + "node_modules/@types/raf": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.0.tgz", + "integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==", + "optional": true + }, "node_modules/@types/react": { "version": "17.0.44", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.44.tgz", @@ -7701,7 +7708,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, "bin": { "atob": "bin/atob.js" }, @@ -8030,6 +8036,15 @@ "node": ">=0.10.0" } }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "optional": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -8381,6 +8396,17 @@ "node-int64": "^0.4.0" } }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/buble": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/buble/-/buble-0.20.0.tgz", @@ -8827,6 +8853,25 @@ "node": ">=6" } }, + "node_modules/canvg": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", + "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/cardinal": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", @@ -10076,7 +10121,7 @@ "version": "3.21.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "funding": { "type": "opencollective", @@ -10220,6 +10265,15 @@ "integrity": "sha512-fkshKv9vV8AmcxkAWVQ9DmEAKiqe09GHdnFaXecp0NIfsGnXIHVJAHfsxdRy9KXV0/KiWdjBqrCYto2fYIO4xQ==", "dev": true }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "optional": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/css-loader": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", @@ -11015,6 +11069,12 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.8.tgz", + "integrity": "sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==", + "optional": true + }, "node_modules/domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", @@ -12444,6 +12504,11 @@ "bser": "2.1.1" } }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -15309,6 +15374,19 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "optional": true, + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/htmlparser2": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", @@ -19227,6 +19305,23 @@ "node": "*" } }, + "node_modules/jspdf": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz", + "integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==", + "dependencies": { + "@babel/runtime": "^7.14.0", + "atob": "^2.1.2", + "btoa": "^1.2.1", + "fflate": "^0.4.8" + }, + "optionalDependencies": { + "canvg": "^3.0.6", + "core-js": "^3.6.0", + "dompurify": "^2.2.0", + "html2canvas": "^1.0.0-rc.5" + } + }, "node_modules/jsprim": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", @@ -25177,7 +25272,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "devOptional": true }, "node_modules/picocolors": { "version": "1.0.0", @@ -25890,7 +25985,7 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "dev": true, + "devOptional": true, "dependencies": { "performance-now": "^2.1.0" } @@ -28006,6 +28101,15 @@ "node": ">=0.10.0" } }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -29186,6 +29290,15 @@ "node": ">=8" } }, + "node_modules/stackblur-canvas": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz", + "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -29631,6 +29744,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/symbol-observable": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-3.0.0.tgz", @@ -29893,6 +30015,15 @@ "node": ">=0.10" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "optional": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -31007,6 +31138,15 @@ "node": ">= 0.4.0" } }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "optional": true, + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -37359,6 +37499,12 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" }, + "@types/raf": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.0.tgz", + "integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==", + "optional": true + }, "@types/react": { "version": "17.0.44", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.44.tgz", @@ -38355,8 +38501,7 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "aws-sign2": { "version": "0.7.0", @@ -38605,6 +38750,12 @@ } } }, + "base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "optional": true + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -38864,6 +39015,11 @@ "node-int64": "^0.4.0" } }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" + }, "buble": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/buble/-/buble-0.20.0.tgz", @@ -39187,6 +39343,22 @@ "simple-get": "^3.0.3" } }, + "canvg": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", + "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", + "optional": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + } + }, "cardinal": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", @@ -40142,7 +40314,7 @@ "version": "3.21.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==", - "dev": true + "devOptional": true }, "core-js-compat": { "version": "3.22.3", @@ -40260,6 +40432,15 @@ "integrity": "sha512-fkshKv9vV8AmcxkAWVQ9DmEAKiqe09GHdnFaXecp0NIfsGnXIHVJAHfsxdRy9KXV0/KiWdjBqrCYto2fYIO4xQ==", "dev": true }, + "css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "optional": true, + "requires": { + "utrie": "^1.0.2" + } + }, "css-loader": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", @@ -40873,6 +41054,12 @@ "domelementtype": "^2.2.0" } }, + "dompurify": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.8.tgz", + "integrity": "sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==", + "optional": true + }, "domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", @@ -41989,6 +42176,11 @@ "bser": "2.1.1" } }, + "fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -44489,6 +44681,16 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "optional": true, + "requires": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + } + }, "htmlparser2": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", @@ -47405,6 +47607,21 @@ "through": ">=2.2.7 <3" } }, + "jspdf": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz", + "integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==", + "requires": { + "@babel/runtime": "^7.14.0", + "atob": "^2.1.2", + "btoa": "^1.2.1", + "canvg": "^3.0.6", + "core-js": "^3.6.0", + "dompurify": "^2.2.0", + "fflate": "^0.4.8", + "html2canvas": "^1.0.0-rc.5" + } + }, "jsprim": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", @@ -51802,7 +52019,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "devOptional": true }, "picocolors": { "version": "1.0.0", @@ -52339,7 +52556,7 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "dev": true, + "devOptional": true, "requires": { "performance-now": "^2.1.0" } @@ -53908,6 +54125,12 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, + "rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "optional": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -54892,6 +55115,12 @@ } } }, + "stackblur-canvas": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz", + "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", + "optional": true + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -55232,6 +55461,12 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "optional": true + }, "symbol-observable": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-3.0.0.tgz", @@ -55420,6 +55655,15 @@ "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", "dev": true }, + "text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "optional": true, + "requires": { + "utrie": "^1.0.2" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -56291,6 +56535,15 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", "dev": true }, + "utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "optional": true, + "requires": { + "base64-arraybuffer": "^1.0.2" + } + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", diff --git a/package.json b/package.json index b32320c108..2e40616245 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "@types/react": "^17.0", "@types/react-dom": "^18.0.2", "@types/react-rnd": "^8.0.0", + "jspdf": "^2.5.1", "lodash": "^4.17.21", "moment": "^2.29.2", "proj4": "^2.7.5", diff --git a/src/Button/PrintButton/InkmapTypes.ts b/src/Button/PrintButton/InkmapTypes.ts index 0f5fb3b664..4c31c0bef7 100644 --- a/src/Button/PrintButton/InkmapTypes.ts +++ b/src/Button/PrintButton/InkmapTypes.ts @@ -5,6 +5,8 @@ export type WmsLayer = { attribution?: string; layer: string; tiled?: boolean; + legendUrl?: string; + layerName?: string; }; export type WmtsLayer = { @@ -18,6 +20,8 @@ export type WmtsLayer = { tileGrid?: any; format?: string; requestEncoding?: string; + legendUrl?: string; + layerName?: string; }; export type GeoJsonLayer = { @@ -25,6 +29,8 @@ export type GeoJsonLayer = { attribution?: string; style: any; geojson: any; + legendUrl?: string; + layerName?: string; }; export type WfsLayer = { @@ -33,6 +39,8 @@ export type WfsLayer = { attribution?: string; layer?: string; projection?: string; + legendUrl?: string; + layerName?: string; }; export type OsmLayer = { @@ -49,6 +57,8 @@ export type OsmLayer = { format?: string; requestEncoding?: string; geojson?: any; + legendUrl?: string; + layerName?: string; }; export type InkmapLayer = WmsLayer | WmtsLayer | GeoJsonLayer | WfsLayer | OsmLayer; diff --git a/src/Button/PrintButton/PrintButton.example.md b/src/Button/PrintButton/PrintButton.example.md index c8ad0029e8..53088deee4 100644 --- a/src/Button/PrintButton/PrintButton.example.md +++ b/src/Button/PrintButton/PrintButton.example.md @@ -13,6 +13,7 @@ import GeoJSON from 'ol/format/GeoJSON'; import OlLayerTile from 'ol/layer/Tile'; import OlMap from 'ol/Map'; import OlSourceOsm from 'ol/source/OSM'; +import OlSourceStamen from 'ol/source/Stamen'; import OlSourceTileWMS from 'ol/source/TileWMS'; import OlView from 'ol/View'; import WMTS from 'ol/source/WMTS'; @@ -124,11 +125,24 @@ const PrintButtonExample = () => { features: new GeoJSON().readFeatures(geojson), }); + const osm = new OlLayerTile({ + source: new OlSourceOsm(), + }); + osm.set('name', 'OpenStreetMap'); + osm.set('legendUrl', '/assets/legend2.png'); + + const stamen = new OlLayerTile({ + source: new OlSourceStamen({ + layer: 'watercolor' + }) + }); + stamen.set('name', 'Stamen'); + stamen.set('legendUrl', '/assets/legend2.png'); + const newMap = new OlMap({ layers: [ - new OlLayerTile({ - source: new OlSourceOsm(), - }), + osm, + stamen, new OlLayerTile({ name: 'True Color Composite', opacity: 0.5, @@ -220,6 +234,7 @@ const PrintButtonExample = () => { /> Print map diff --git a/src/Button/PrintButton/PrintButton.tsx b/src/Button/PrintButton/PrintButton.tsx index a641b95ad2..1dfc535640 100644 --- a/src/Button/PrintButton/PrintButton.tsx +++ b/src/Button/PrintButton/PrintButton.tsx @@ -2,11 +2,14 @@ import React, { useState } from 'react'; +import { jsPDF } from 'jspdf'; + import { useMap } from '../../Hook/useMap'; import { downloadBlob, - queuePrint, - getJobStatus + getAttributionsText, + getJobStatus, + queuePrint } from '@camptocamp/inkmap'; import SimpleButton, { SimpleButtonProps @@ -23,11 +26,14 @@ import { interface OwnProps { onProgressChange?: (val: number) => void; outputFileName?: string; + format?: 'png' | 'pdf'; dpi?: number; size?: InkmapPrintSpec['size']; northArrow?: InkmapPrintSpec['northArrow']; attributions?: InkmapPrintSpec['attributions']; scaleBar?: InkmapPrintSpec['scaleBar']; + title?: string; + legendTitle?: string; } export type PrintButtonProps = OwnProps & SimpleButtonProps; @@ -41,18 +47,18 @@ const PrintButton: React.FC = ({ }, dpi = 120, onProgressChange, - outputFileName = 'react-geo-image.png', + outputFileName = 'react-geo-image', size = [400, 240, 'mm'], + format = 'png', + title = 'Print', + legendTitle= 'Legend', ...passThroughProps }) => { const [loading, setLoading] = useState(false); const map = useMap(); - const onPrintClick = async (event: any) => { - if (!map) { - return; - } + const printPng = async () => { setLoading(true); const printConfigByMap = await MapUtil.generatePrintConfig(map); const printConfig = { @@ -75,7 +81,100 @@ const PrintButton: React.FC = ({ if (printStatus.progress === 1) { // job is finished setLoading(false); - downloadBlob(printStatus.imageBlob, outputFileName); + downloadBlob(printStatus.imageBlob, `${outputFileName}.${format}`); + } + + if (printStatus.sourceLoadErrors.length > 0) { + // display errors + let errorMessage = ''; + printStatus.sourceLoadErrors.forEach((element: any) => { + errorMessage = `${errorMessage} - ${element.url}`; + }); + Logger.error('Inkmap error: ', errorMessage); + } + }); + } + + const printPdf = async () => { + setLoading(true); + const printConfigByMap = await MapUtil.generatePrintConfig(map); + const mapWidth = 277; // mm + const mapHeight = 170; // mm + + // Force map size to fit the PDF document + const pdfSpec: InkmapPrintSpec = { + ...printConfigByMap, + size: [mapWidth, mapHeight, 'mm'], + dpi, + scaleBar: scaleBar, + northArrow: northArrow, + attributions: false + }; + + // create a job, get a promise that resolves when the job is finished + const jobId = await queuePrint(pdfSpec); + + getJobStatus(jobId).subscribe((printStatus: any) => { + // update the job progress + const progressPercent = Math.round(printStatus.progress * 100); + if (onProgressChange) { + onProgressChange(progressPercent); + } + + if (printStatus.progress === 1) { + // initializes the PDF document + const doc = new jsPDF({ + orientation: 'landscape', + unit: 'mm', + format: 'a4', // 210 by 297mm + putOnlyUsedFonts: true + }); + + // create an Object URL from the map image blob and add it to the PDF + const imgUrl = URL.createObjectURL(printStatus.imageBlob); + doc.addImage(imgUrl, 'PNG', 10, 17, mapWidth, mapHeight); + + // add a title + doc.setFont('arial', 'bold'); + doc.setFontSize(20); + doc.text(title, 148.5, 13, { align: 'center' }); + + // add attribution + doc.setFont('arial', 'normal'); + doc.setFontSize(10); + doc.text(getAttributionsText(pdfSpec), 287, 200, { align: 'right' }); + + + if (pdfSpec.layers.some(l => l.legendUrl && l.legendUrl.length > 0)) { + // second page for legends + doc.addPage('a4', 'l'); + + pdfSpec.layers.forEach((layer, idx) => { + const legendUrl = layer.legendUrl; + const name = layer.layerName; + const xPosition = 20 + (60 * idx); // todo: dynamic size + if (legendUrl && name) { + // todo: use actual legend image size + doc.setFont('arial', 'bold'); + doc.setFontSize(12); + doc.text(name, xPosition, 17, { align: 'left' }); + doc.addImage(legendUrl, 'PNG', xPosition, 20, 60, 60); + } + }); + + // add a title + doc.setFont('arial', 'bold'); + doc.setFontSize(20); + doc.text(legendTitle, 148.5, 13, { align: 'center' }); + } + + // job is finished + setLoading(false); + + // download the result + // todo: only one pdf + doc.save(`${outputFileName}.${format}`); + // legendsDoc.save(`${outputFileName}-legend.${format}`); } if (printStatus.sourceLoadErrors.length > 0) { @@ -87,6 +186,17 @@ const PrintButton: React.FC = ({ Logger.error('Inkmap error: ', errorMessage); } }); + } + + const onPrintClick = async () => { + if (!map) { + return; + } + if (format === 'png') { + await printPng(); + } else { + await printPdf(); + } }; if (!map) {