From be574df599a107c1569650a018b38098880793ca Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Sat, 2 Aug 2025 17:29:38 +0000 Subject: [PATCH] feat(image): add progressive JPEG generation support - Add convertPDFToJpegBytes method for progressive JPEG images - Integrate @push.rocks/smartjimp for true progressive encoding - Update readme with comprehensive documentation - Update legal section to Task Venture Capital GmbH --- package.json | 3 +- pnpm-lock.yaml | 839 +++++++++++++++++++++++++++++++- pnpm-workspace.yaml | 2 + readme.md | 741 ++++++++++++++-------------- test/test.ts | 107 ++++ ts/smartpdf.classes.smartpdf.ts | 48 ++ ts/smartpdf.plugins.ts | 2 + 7 files changed, 1358 insertions(+), 384 deletions(-) create mode 100644 pnpm-workspace.yaml diff --git a/package.json b/package.json index c79d950..f763041 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "author": "Lossless GmbH", "license": "MIT", "scripts": { - "test": "(tstest test/ --verbose --timeout 60)", + "test": "(tstest test/ --verbose --timeout 120)", "build": "(tsbuild tsfolders --allowimplicitany)", "buildDocs": "tsdoc" }, @@ -24,6 +24,7 @@ "@push.rocks/smartbuffer": "^3.0.5", "@push.rocks/smartdelay": "^3.0.5", "@push.rocks/smartfile": "^11.2.5", + "@push.rocks/smartjimp": "^1.2.0", "@push.rocks/smartnetwork": "^4.1.2", "@push.rocks/smartpath": "^6.0.0", "@push.rocks/smartpromise": "^4.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5210bed..316371b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@push.rocks/smartfile': specifier: ^11.2.5 version: 11.2.5 + '@push.rocks/smartjimp': + specifier: ^1.2.0 + version: 1.2.0 '@push.rocks/smartnetwork': specifier: ^4.1.2 version: 4.1.2 @@ -53,7 +56,7 @@ importers: version: 2.6.4 '@git.zone/tsdoc': specifier: ^1.5.0 - version: 1.5.0(ws@8.18.3)(zod@3.24.2) + version: 1.5.0(ws@8.18.3)(zod@3.25.76) '@git.zone/tsrun': specifier: ^1.3.3 version: 1.3.3 @@ -622,6 +625,128 @@ packages: resolution: {integrity: sha512-mfOoUlIw8VBiJYPrl5RZfMzkXC/z7gbSpi2ecycrj/gRWLq2CMV+Q+0G+JPjeOmuNFgg0skEIzkVFzVYFP6URw==} engines: {node: '>=18.0.0'} + '@img/sharp-darwin-arm64@0.34.3': + resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.3': + resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.0': + resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.0': + resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.0': + resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.0': + resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.0': + resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.0': + resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.0': + resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.3': + resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.3': + resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.3': + resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.3': + resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.3': + resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.3': + resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.3': + resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.3': + resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.3': + resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.3': + resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.3': + resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@inquirer/checkbox@3.0.1': resolution: {integrity: sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==} engines: {node: '>=18'} @@ -690,6 +815,118 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@jimp/core@1.6.0': + resolution: {integrity: sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==} + engines: {node: '>=18'} + + '@jimp/diff@1.6.0': + resolution: {integrity: sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==} + engines: {node: '>=18'} + + '@jimp/file-ops@1.6.0': + resolution: {integrity: sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==} + engines: {node: '>=18'} + + '@jimp/js-bmp@1.6.0': + resolution: {integrity: sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==} + engines: {node: '>=18'} + + '@jimp/js-gif@1.6.0': + resolution: {integrity: sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==} + engines: {node: '>=18'} + + '@jimp/js-jpeg@1.6.0': + resolution: {integrity: sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==} + engines: {node: '>=18'} + + '@jimp/js-png@1.6.0': + resolution: {integrity: sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==} + engines: {node: '>=18'} + + '@jimp/js-tiff@1.6.0': + resolution: {integrity: sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==} + engines: {node: '>=18'} + + '@jimp/plugin-blit@1.6.0': + resolution: {integrity: sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==} + engines: {node: '>=18'} + + '@jimp/plugin-blur@1.6.0': + resolution: {integrity: sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==} + engines: {node: '>=18'} + + '@jimp/plugin-circle@1.6.0': + resolution: {integrity: sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==} + engines: {node: '>=18'} + + '@jimp/plugin-color@1.6.0': + resolution: {integrity: sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==} + engines: {node: '>=18'} + + '@jimp/plugin-contain@1.6.0': + resolution: {integrity: sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==} + engines: {node: '>=18'} + + '@jimp/plugin-cover@1.6.0': + resolution: {integrity: sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==} + engines: {node: '>=18'} + + '@jimp/plugin-crop@1.6.0': + resolution: {integrity: sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==} + engines: {node: '>=18'} + + '@jimp/plugin-displace@1.6.0': + resolution: {integrity: sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==} + engines: {node: '>=18'} + + '@jimp/plugin-dither@1.6.0': + resolution: {integrity: sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==} + engines: {node: '>=18'} + + '@jimp/plugin-fisheye@1.6.0': + resolution: {integrity: sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==} + engines: {node: '>=18'} + + '@jimp/plugin-flip@1.6.0': + resolution: {integrity: sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==} + engines: {node: '>=18'} + + '@jimp/plugin-hash@1.6.0': + resolution: {integrity: sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==} + engines: {node: '>=18'} + + '@jimp/plugin-mask@1.6.0': + resolution: {integrity: sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==} + engines: {node: '>=18'} + + '@jimp/plugin-print@1.6.0': + resolution: {integrity: sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==} + engines: {node: '>=18'} + + '@jimp/plugin-quantize@1.6.0': + resolution: {integrity: sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==} + engines: {node: '>=18'} + + '@jimp/plugin-resize@1.6.0': + resolution: {integrity: sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==} + engines: {node: '>=18'} + + '@jimp/plugin-rotate@1.6.0': + resolution: {integrity: sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==} + engines: {node: '>=18'} + + '@jimp/plugin-threshold@1.6.0': + resolution: {integrity: sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==} + engines: {node: '>=18'} + + '@jimp/types@1.6.0': + resolution: {integrity: sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==} + engines: {node: '>=18'} + + '@jimp/utils@1.6.0': + resolution: {integrity: sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==} + engines: {node: '>=18'} + '@koa/router@9.4.0': resolution: {integrity: sha512-dOOXgzqaDoHu5qqMEPLKEgLz5CeIA7q8+1W62mCvFVCOqeC71UoTGJ4u1xUSOpIl2J1x2pqrNULkFteUeZW3/A==} engines: {node: '>= 8.0.0'} @@ -888,6 +1125,9 @@ packages: '@push.rocks/smartinteract@2.0.16': resolution: {integrity: sha512-eltvVRRUKBKd77DSFA4DPY2g4V4teZLNe8A93CDy/WglglYcUjxMoLY/b0DFTWCWKYT+yjk6Fe6p0FRrvX9Yvg==} + '@push.rocks/smartjimp@1.2.0': + resolution: {integrity: sha512-SPz8p2ZuphNqIXK/UDsNFrnpJn/jr6FbuBSMQc0V2v2ffQIF32ZqktKQpXpitiqD1K5JEYS56JAhlYHgrAu7yw==} + '@push.rocks/smartjson@5.0.20': resolution: {integrity: sha512-ogGBLyOTluphZVwBYNyjhm5sziPGuiAwWihW07OSRxD4HQUyqj9Ek6r1pqH07JUG5EbtRYivM1Yt1cCwnu3JVQ==} @@ -963,6 +1203,9 @@ packages: '@push.rocks/smartrequest@2.1.0': resolution: {integrity: sha512-3eHLTRInHA+u+W98TqJwgTES7rRimBAsJC4JxVNQC3UUezmblAhM5/TIQsEBQTsbjAY8SeQKy6NHzW6iTiaD8w==} + '@push.rocks/smartrequest@4.2.1': + resolution: {integrity: sha512-33sxhXMOwDx2tv98LlyxDxI/UTjw16BOSWbnqrdUdNby/sSP3ahW3NF4JMOU5xKNQUz7TjLgREj9dPuumEgQ/g==} + '@push.rocks/smartrouter@1.3.3': resolution: {integrity: sha512-1+xZEnWlhzqLWAaJ1zFNhQ0zgbfCWQl1DBT72LygLxTs+P0K8AwJKgqo/IX6CT55kGCFnPAZIYSbVJlGsgrB0w==} @@ -1654,6 +1897,9 @@ packages: '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} + '@types/node@16.9.1': + resolution: {integrity: sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==} + '@types/node@22.17.0': resolution: {integrity: sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==} @@ -1738,6 +1984,10 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -1790,6 +2040,9 @@ packages: resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==} engines: {node: '>=14'} + any-base@1.1.0: + resolution: {integrity: sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -1819,6 +2072,10 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + await-to-js@3.0.0: + resolution: {integrity: sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==} + engines: {node: '>=6.0.0'} + b4a@1.6.7: resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} @@ -1864,6 +2121,9 @@ packages: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} + bmp-ts@1.0.9: + resolution: {integrity: sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==} + body-parser@1.20.3: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -2035,6 +2295,10 @@ packages: color@3.2.1: resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + colorspace@1.1.4: resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} @@ -2238,6 +2502,10 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -2399,9 +2667,20 @@ packages: resolution: {integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=} engines: {node: '>= 0.6'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + exif-parser@0.1.12: + resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==} + express-force-ssl@0.3.2: resolution: {integrity: sha1-AbK0mK5v0uQRUrIrV6Phc3c69n4=} engines: {node: '>=0.2.2'} @@ -2472,6 +2751,10 @@ packages: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} + file-type@16.5.4: + resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} + engines: {node: '>=10'} + file-type@19.6.0: resolution: {integrity: sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ==} engines: {node: '>=18'} @@ -2613,6 +2896,9 @@ packages: resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==} engines: {node: '>= 14'} + gifwrap@0.10.1: + resolution: {integrity: sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==} + glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true @@ -2753,6 +3039,9 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + image-q@4.0.0: + resolution: {integrity: sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -2903,6 +3192,13 @@ packages: resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} + jimp@1.6.0: + resolution: {integrity: sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==} + engines: {node: '>=18'} + + jpeg-js@0.4.4: + resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} + js-base64@3.7.7: resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} @@ -3265,6 +3561,11 @@ packages: engines: {node: '>=4'} hasBin: true + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + mime@4.0.7: resolution: {integrity: sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==} engines: {node: '>=16'} @@ -3433,6 +3734,9 @@ packages: observable-fns@0.6.1: resolution: {integrity: sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==} + omggif@1.0.10: + resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -3522,6 +3826,15 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-bmfont-ascii@1.0.6: + resolution: {integrity: sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==} + + parse-bmfont-binary@1.0.6: + resolution: {integrity: sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==} + + parse-bmfont-xml@1.1.6: + resolution: {integrity: sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==} + parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -3594,6 +3907,10 @@ packages: hasBin: true bundledDependencies: [] + peek-readable@4.1.0: + resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} + engines: {node: '>=8'} + peek-readable@5.4.2: resolution: {integrity: sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg==} engines: {node: '>=14.16'} @@ -3619,10 +3936,22 @@ packages: resolution: {integrity: sha512-56ZMC0j7SCsMMLdOoUg12VZCfj/+ZO+yfOSjaNCRrmZZr6GLbN2X/Ui56T15dI8NhiHckaw5X2pvyfAomanwqQ==} engines: {node: '>=4.0.0'} + pixelmatch@5.3.0: + resolution: {integrity: sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==} + hasBin: true + pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + pngjs@6.0.0: + resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} + engines: {node: '>=12.13.0'} + + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -3642,6 +3971,10 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process@0.11.10: + resolution: {integrity: sha1-czIwDoQBYb2j5podHZGn1LwW8YI=} + engines: {node: '>= 0.6.0'} + progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -3739,6 +4072,14 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readable-web-to-node-stream@3.0.4: + resolution: {integrity: sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==} + engines: {node: '>=8'} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -3884,6 +4225,10 @@ packages: engines: {node: '>= 0.10'} hasBin: true + sharp@0.34.3: + resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -3924,6 +4269,10 @@ packages: simple-swizzle@0.2.2: resolution: {integrity: sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=} + simple-xml-to-json@1.2.3: + resolution: {integrity: sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA==} + engines: {node: '>=20.12.2'} + smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -4035,6 +4384,10 @@ packages: strnum@1.1.2: resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} + strtok3@6.3.0: + resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} + engines: {node: '>=10'} + strtok3@9.1.1: resolution: {integrity: sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw==} engines: {node: '>=16'} @@ -4091,6 +4444,9 @@ packages: tiny-worker@2.3.0: resolution: {integrity: sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==} + tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -4103,6 +4459,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + token-types@4.2.1: + resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} + engines: {node: '>=10'} + token-types@6.0.4: resolution: {integrity: sha512-MD9MjpVNhVyH4fyd5rKphjvt/1qj+PtQUz65aFqAZA6XniWAuSFRjLk3e2VALEFlh9OwBpXUN7rfeqSnT/Fmkw==} engines: {node: '>=14.16'} @@ -4247,6 +4607,9 @@ packages: resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} engines: {node: '>= 0.4'} + utif2@4.1.0: + resolution: {integrity: sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==} + util-deprecate@1.0.2: resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} @@ -4366,6 +4729,9 @@ packages: resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} hasBin: true + xml-parse-from-string@1.0.1: + resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==} + xml2js@0.5.0: resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} engines: {node: '>=4.0.0'} @@ -4424,6 +4790,9 @@ packages: zod@3.24.2: resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -5329,13 +5698,13 @@ snapshots: - '@swc/helpers' - supports-color - '@git.zone/tsdoc@1.5.0(ws@8.18.3)(zod@3.24.2)': + '@git.zone/tsdoc@1.5.0(ws@8.18.3)(zod@3.25.76)': dependencies: '@git.zone/tspublish': 1.9.1 '@push.rocks/early': 4.0.4 '@push.rocks/npmextra': 5.1.2 '@push.rocks/qenv': 6.1.0 - '@push.rocks/smartai': 0.5.5(typescript@5.8.3)(ws@8.18.3)(zod@3.24.2) + '@push.rocks/smartai': 0.5.5(typescript@5.8.3)(ws@8.18.3)(zod@3.25.76) '@push.rocks/smartcli': 4.0.11 '@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartfile': 11.2.5 @@ -5426,6 +5795,92 @@ snapshots: dependencies: happy-dom: 15.11.7 + '@img/sharp-darwin-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.0 + optional: true + + '@img/sharp-darwin-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.0 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.0': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.0': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.0': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + optional: true + + '@img/sharp-linux-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.0 + optional: true + + '@img/sharp-linux-arm@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.0 + optional: true + + '@img/sharp-linux-ppc64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.0 + optional: true + + '@img/sharp-linux-s390x@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.0 + optional: true + + '@img/sharp-linux-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.0 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + optional: true + + '@img/sharp-wasm32@0.34.3': + dependencies: + '@emnapi/runtime': 1.4.5 + optional: true + + '@img/sharp-win32-arm64@0.34.3': + optional: true + + '@img/sharp-win32-ia32@0.34.3': + optional: true + + '@img/sharp-win32-x64@0.34.3': + optional: true + '@inquirer/checkbox@3.0.1': dependencies: '@inquirer/core': 9.2.1 @@ -5537,6 +5992,195 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jimp/core@1.6.0': + dependencies: + '@jimp/file-ops': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + await-to-js: 3.0.0 + exif-parser: 0.1.12 + file-type: 16.5.4 + mime: 3.0.0 + + '@jimp/diff@1.6.0': + dependencies: + '@jimp/plugin-resize': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + pixelmatch: 5.3.0 + + '@jimp/file-ops@1.6.0': {} + + '@jimp/js-bmp@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + bmp-ts: 1.0.9 + + '@jimp/js-gif@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + gifwrap: 0.10.1 + omggif: 1.0.10 + + '@jimp/js-jpeg@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + jpeg-js: 0.4.4 + + '@jimp/js-png@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + pngjs: 7.0.0 + + '@jimp/js-tiff@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + utif2: 4.1.0 + + '@jimp/plugin-blit@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-blur@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/utils': 1.6.0 + + '@jimp/plugin-circle@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-color@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + tinycolor2: 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-contain@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/plugin-blit': 1.6.0 + '@jimp/plugin-resize': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-cover@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/plugin-crop': 1.6.0 + '@jimp/plugin-resize': 1.6.0 + '@jimp/types': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-crop@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-displace@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-dither@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + + '@jimp/plugin-fisheye@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-flip@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-hash@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/js-bmp': 1.6.0 + '@jimp/js-jpeg': 1.6.0 + '@jimp/js-png': 1.6.0 + '@jimp/js-tiff': 1.6.0 + '@jimp/plugin-color': 1.6.0 + '@jimp/plugin-resize': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + any-base: 1.1.0 + + '@jimp/plugin-mask@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-print@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/js-jpeg': 1.6.0 + '@jimp/js-png': 1.6.0 + '@jimp/plugin-blit': 1.6.0 + '@jimp/types': 1.6.0 + parse-bmfont-ascii: 1.0.6 + parse-bmfont-binary: 1.0.6 + parse-bmfont-xml: 1.1.6 + simple-xml-to-json: 1.2.3 + zod: 3.25.76 + + '@jimp/plugin-quantize@1.6.0': + dependencies: + image-q: 4.0.0 + zod: 3.25.76 + + '@jimp/plugin-resize@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/types': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-rotate@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/plugin-crop': 1.6.0 + '@jimp/plugin-resize': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/plugin-threshold@1.6.0': + dependencies: + '@jimp/core': 1.6.0 + '@jimp/plugin-color': 1.6.0 + '@jimp/plugin-hash': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + zod: 3.25.76 + + '@jimp/types@1.6.0': + dependencies: + zod: 3.25.76 + + '@jimp/utils@1.6.0': + dependencies: + '@jimp/types': 1.6.0 + tinycolor2: 1.6.0 + '@koa/router@9.4.0': dependencies: debug: 4.4.1 @@ -5743,7 +6387,7 @@ snapshots: '@push.rocks/smartlog': 3.1.8 '@push.rocks/smartpath': 5.1.0 - '@push.rocks/smartai@0.5.5(typescript@5.8.3)(ws@8.18.3)(zod@3.24.2)': + '@push.rocks/smartai@0.5.5(typescript@5.8.3)(ws@8.18.3)(zod@3.25.76)': dependencies: '@anthropic-ai/sdk': 0.57.0 '@push.rocks/smartarray': 1.1.0 @@ -5753,7 +6397,7 @@ snapshots: '@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartrequest': 2.1.0 '@push.rocks/webstream': 1.0.10 - openai: 5.11.0(ws@8.18.3)(zod@3.24.2) + openai: 5.11.0(ws@8.18.3)(zod@3.25.76) transitivePeerDependencies: - bare-buffer - bufferutil @@ -5981,6 +6625,18 @@ snapshots: '@push.rocks/smartpromise': 4.2.3 inquirer: 11.1.0 + '@push.rocks/smartjimp@1.2.0': + dependencies: + '@push.rocks/levelcache': 3.1.1 + '@push.rocks/smartfile': 11.2.5 + '@push.rocks/smarthash': 3.2.0 + '@push.rocks/smartpath': 6.0.0 + '@push.rocks/smartrequest': 4.2.1 + jimp: 1.6.0 + sharp: 0.34.3 + transitivePeerDependencies: + - aws-crt + '@push.rocks/smartjson@5.0.20': dependencies: '@push.rocks/smartenv': 5.0.12 @@ -6182,6 +6838,15 @@ snapshots: agentkeepalive: 4.6.0 form-data: 4.0.4 + '@push.rocks/smartrequest@4.2.1': + dependencies: + '@push.rocks/smartenv': 5.0.13 + '@push.rocks/smartpath': 6.0.0 + '@push.rocks/smartpromise': 4.2.3 + '@push.rocks/smarturl': 3.1.0 + agentkeepalive: 4.6.0 + form-data: 4.0.4 + '@push.rocks/smartrouter@1.3.3': dependencies: '@push.rocks/lik': 6.2.2 @@ -7157,6 +7822,8 @@ snapshots: dependencies: '@types/node': 24.1.0 + '@types/node@16.9.1': {} + '@types/node@22.17.0': dependencies: undici-types: 6.21.0 @@ -7242,6 +7909,10 @@ snapshots: '@ungap/structured-clone@1.3.0': {} + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -7285,6 +7956,8 @@ snapshots: ansis@4.1.0: {} + any-base@1.1.0: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -7311,6 +7984,8 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + await-to-js@3.0.0: {} + b4a@1.6.7: {} bail@2.0.2: {} @@ -7350,6 +8025,8 @@ snapshots: basic-ftp@5.0.5: {} + bmp-ts@1.0.9: {} + body-parser@1.20.3: dependencies: bytes: 3.1.2 @@ -7550,6 +8227,11 @@ snapshots: color-convert: 1.9.3 color-string: 1.9.1 + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + colorspace@1.1.4: dependencies: color: 3.2.1 @@ -7701,6 +8383,8 @@ snapshots: destroy@1.2.0: {} + detect-libc@2.0.4: {} + devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -7901,8 +8585,14 @@ snapshots: etag@1.8.1: {} + event-target-shim@5.0.1: {} + eventemitter3@4.0.7: {} + events@3.3.0: {} + + exif-parser@0.1.12: {} + express-force-ssl@0.3.2: dependencies: lodash.assign: 3.2.0 @@ -8033,6 +8723,12 @@ snapshots: dependencies: is-unicode-supported: 2.1.0 + file-type@16.5.4: + dependencies: + readable-web-to-node-stream: 3.0.4 + strtok3: 6.3.0 + token-types: 4.2.1 + file-type@19.6.0: dependencies: get-stream: 9.0.1 @@ -8206,6 +8902,11 @@ snapshots: transitivePeerDependencies: - supports-color + gifwrap@0.10.1: + dependencies: + image-q: 4.0.0 + omggif: 1.0.10 + glob@10.4.5: dependencies: foreground-child: 3.3.1 @@ -8406,6 +9107,10 @@ snapshots: ignore@5.3.2: {} + image-q@4.0.0: + dependencies: + '@types/node': 16.9.1 + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -8545,6 +9250,38 @@ snapshots: dependencies: '@isaacs/cliui': 8.0.2 + jimp@1.6.0: + dependencies: + '@jimp/core': 1.6.0 + '@jimp/diff': 1.6.0 + '@jimp/js-bmp': 1.6.0 + '@jimp/js-gif': 1.6.0 + '@jimp/js-jpeg': 1.6.0 + '@jimp/js-png': 1.6.0 + '@jimp/js-tiff': 1.6.0 + '@jimp/plugin-blit': 1.6.0 + '@jimp/plugin-blur': 1.6.0 + '@jimp/plugin-circle': 1.6.0 + '@jimp/plugin-color': 1.6.0 + '@jimp/plugin-contain': 1.6.0 + '@jimp/plugin-cover': 1.6.0 + '@jimp/plugin-crop': 1.6.0 + '@jimp/plugin-displace': 1.6.0 + '@jimp/plugin-dither': 1.6.0 + '@jimp/plugin-fisheye': 1.6.0 + '@jimp/plugin-flip': 1.6.0 + '@jimp/plugin-hash': 1.6.0 + '@jimp/plugin-mask': 1.6.0 + '@jimp/plugin-print': 1.6.0 + '@jimp/plugin-quantize': 1.6.0 + '@jimp/plugin-resize': 1.6.0 + '@jimp/plugin-rotate': 1.6.0 + '@jimp/plugin-threshold': 1.6.0 + '@jimp/types': 1.6.0 + '@jimp/utils': 1.6.0 + + jpeg-js@0.4.4: {} + js-base64@3.7.7: {} js-tokens@4.0.0: {} @@ -9104,6 +9841,8 @@ snapshots: mime@1.6.0: {} + mime@3.0.0: {} + mime@4.0.7: {} mimic-response@3.1.0: {} @@ -9257,6 +9996,8 @@ snapshots: observable-fns@0.6.1: {} + omggif@1.0.10: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -9277,10 +10018,10 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@5.11.0(ws@8.18.3)(zod@3.24.2): + openai@5.11.0(ws@8.18.3)(zod@3.25.76): optionalDependencies: ws: 8.18.3 - zod: 3.24.2 + zod: 3.25.76 os-tmpdir@1.0.2: {} @@ -9346,6 +10087,15 @@ snapshots: dependencies: callsites: 3.1.0 + parse-bmfont-ascii@1.0.6: {} + + parse-bmfont-binary@1.0.6: {} + + parse-bmfont-xml@1.1.6: + dependencies: + xml-parse-from-string: 1.0.1 + xml2js: 0.5.0 + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.26.2 @@ -9398,6 +10148,8 @@ snapshots: pdf2json@3.2.0: {} + peek-readable@4.1.0: {} + peek-readable@5.4.2: {} peek-stream@1.1.3: @@ -9416,10 +10168,18 @@ snapshots: ping@0.4.4: {} + pixelmatch@5.3.0: + dependencies: + pngjs: 6.0.0 + pkg-dir@4.2.0: dependencies: find-up: 4.1.0 + pngjs@6.0.0: {} + + pngjs@7.0.0: {} + possible-typed-array-names@1.1.0: {} pretty-ms@7.0.1: @@ -9436,6 +10196,8 @@ snapshots: process-nextick-args@2.0.1: {} + process@0.11.10: {} + progress@2.0.3: {} property-information@7.1.0: {} @@ -9581,6 +10343,18 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readable-web-to-node-stream@3.0.4: + dependencies: + readable-stream: 4.7.0 + readdirp@4.1.2: {} regenerator-runtime@0.14.1: {} @@ -9806,6 +10580,35 @@ snapshots: safe-buffer: 5.2.1 to-buffer: 1.2.1 + sharp@0.34.3: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.3 + '@img/sharp-darwin-x64': 0.34.3 + '@img/sharp-libvips-darwin-arm64': 1.2.0 + '@img/sharp-libvips-darwin-x64': 1.2.0 + '@img/sharp-libvips-linux-arm': 1.2.0 + '@img/sharp-libvips-linux-arm64': 1.2.0 + '@img/sharp-libvips-linux-ppc64': 1.2.0 + '@img/sharp-libvips-linux-s390x': 1.2.0 + '@img/sharp-libvips-linux-x64': 1.2.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + '@img/sharp-linux-arm': 0.34.3 + '@img/sharp-linux-arm64': 0.34.3 + '@img/sharp-linux-ppc64': 0.34.3 + '@img/sharp-linux-s390x': 0.34.3 + '@img/sharp-linux-x64': 0.34.3 + '@img/sharp-linuxmusl-arm64': 0.34.3 + '@img/sharp-linuxmusl-x64': 0.34.3 + '@img/sharp-wasm32': 0.34.3 + '@img/sharp-win32-arm64': 0.34.3 + '@img/sharp-win32-ia32': 0.34.3 + '@img/sharp-win32-x64': 0.34.3 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -9856,6 +10659,8 @@ snapshots: dependencies: is-arrayish: 0.3.2 + simple-xml-to-json@1.2.3: {} + smart-buffer@4.2.0: {} socket.io-adapter@2.5.5: @@ -9995,6 +10800,11 @@ snapshots: strnum@1.1.2: {} + strtok3@6.3.0: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 4.1.0 + strtok3@9.1.1: dependencies: '@tokenizer/token': 0.3.0 @@ -10075,6 +10885,8 @@ snapshots: dependencies: esm: 3.2.25 + tinycolor2@1.6.0: {} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -10087,6 +10899,11 @@ snapshots: toidentifier@1.0.1: {} + token-types@4.2.1: + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + token-types@6.0.4: dependencies: '@tokenizer/token': 0.3.0 @@ -10221,6 +11038,10 @@ snapshots: punycode: 1.4.1 qs: 6.14.0 + utif2@4.1.0: + dependencies: + pako: 1.0.11 + util-deprecate@1.0.2: {} utils-merge@1.0.1: {} @@ -10325,6 +11146,8 @@ snapshots: dependencies: sax: 1.4.1 + xml-parse-from-string@1.0.1: {} + xml2js@0.5.0: dependencies: sax: 1.4.1 @@ -10377,4 +11200,6 @@ snapshots: zod@3.24.2: {} + zod@3.25.76: {} + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..e6654d6 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +onlyBuiltDependencies: + - sharp diff --git a/readme.md b/readme.md index a043509..3b7188a 100644 --- a/readme.md +++ b/readme.md @@ -1,420 +1,409 @@ -# @push.rocks/smartpdf -Create PDFs on the fly from HTML, websites, or existing PDFs with advanced features like text extraction, PDF merging, and PNG conversion. +# @push.rocks/smartpdf 📄✨ -## Install -To install `@push.rocks/smartpdf`, use npm or yarn: +> **Transform HTML, websites, and PDFs into beautiful documents with just a few lines of code!** + +[![npm version](https://img.shields.io/npm/v/@push.rocks/smartpdf.svg?style=flat-square)](https://www.npmjs.com/package/@push.rocks/smartpdf) +[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg?style=flat-square)](https://www.typescriptlang.org/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](./license) + +## 🚀 Why SmartPDF? + +SmartPDF is your Swiss Army knife for PDF operations in Node.js. Whether you're generating invoices, creating reports, or converting web pages to PDFs, we've got you covered with a simple, powerful API. + +### ✨ Features at a Glance + +- 📝 **HTML to PDF** - Transform HTML strings with full CSS support +- 🌐 **Website to PDF** - Capture any website as a perfectly formatted PDF +- 🔀 **PDF Merging** - Combine multiple PDFs into one +- 🖼️ **PDF to Images** - Convert PDFs to PNG, WebP, or progressive JPEG +- 📑 **Text Extraction** - Pull text content from existing PDFs +- 🎯 **Smart Port Management** - Automatic port allocation for concurrent instances +- 💪 **TypeScript First** - Full type safety and IntelliSense support +- ⚡ **High Performance** - Optimized for speed and reliability + +## 📦 Installation ```bash +# Using npm npm install @push.rocks/smartpdf --save -``` -Or with yarn: - -```bash +# Using yarn yarn add @push.rocks/smartpdf + +# Using pnpm (recommended) +pnpm add @push.rocks/smartpdf ``` -## Requirements -This package requires a Chrome or Chromium installation to be available on the system, as it uses Puppeteer for rendering. The package will automatically detect and use the appropriate executable. - -## Usage -`@push.rocks/smartpdf` provides a powerful interface for PDF generation and manipulation. All examples use ESM syntax and TypeScript. - -### Getting Started -First, import the necessary classes: +## 🎯 Quick Start ```typescript -import { SmartPdf, IPdf } from '@push.rocks/smartpdf'; +import { SmartPdf } from '@push.rocks/smartpdf'; + +// Create and start SmartPdf +const smartPdf = await SmartPdf.create(); +await smartPdf.start(); + +// Generate a PDF from HTML +const pdf = await smartPdf.getA4PdfResultForHtmlString(` +

Hello, PDF World! 🌍

+

This is my first SmartPDF document.

+`); + +// Save it +await fs.writeFile('my-first-pdf.pdf', pdf.buffer); + +// Don't forget to clean up! +await smartPdf.stop(); ``` -### Basic Setup with Automatic Port Allocation -SmartPdf automatically finds an available port between 20000-30000 for its internal server: +## 📚 Core Concepts + +### 🏗️ Instance Management + +SmartPDF uses a client-server architecture for maximum performance. Always remember: + +1. **Create** an instance +2. **Start** the server +3. **Do your PDF magic** +4. **Stop** the server ```typescript -async function setupSmartPdf() { - const smartPdf = await SmartPdf.create(); - await smartPdf.start(); - - // Your PDF operations here - - await smartPdf.stop(); -} +const smartPdf = await SmartPdf.create(); +await smartPdf.start(); +// ... your PDF operations ... +await smartPdf.stop(); ``` -### Advanced Setup with Custom Port Configuration -You can specify custom port settings to avoid conflicts or meet specific requirements: +### 🔌 Smart Port Allocation + +Run multiple instances without port conflicts: ```typescript -// Use a specific port -const smartPdf = await SmartPdf.create({ port: 3000 }); +// Each instance automatically finds a free port +const instance1 = await SmartPdf.create(); // Port: 20000 +const instance2 = await SmartPdf.create(); // Port: 20001 +const instance3 = await SmartPdf.create(); // Port: 20002 -// Use a custom port range -const smartPdf = await SmartPdf.create({ - portRangeStart: 4000, - portRangeEnd: 5000 +// Or specify custom settings +const customInstance = await SmartPdf.create({ + port: 3000, // Use specific port + portRangeStart: 4000, // Or define a range + portRangeEnd: 5000 +}); +``` + +## 🎨 PDF Generation + +### 📝 From HTML String + +Create beautiful PDFs from HTML with full CSS support: + +```typescript +const smartPdf = await SmartPdf.create(); +await smartPdf.start(); + +const pdf = await smartPdf.getA4PdfResultForHtmlString(` + + + + + + +
+

Invoice #2024-001

+

Generated on ${new Date().toLocaleDateString()}

+
+
+

Bill To:

+

Acme Corporation

+

Total: $1,234.56

+
+ + +`); + +await fs.writeFile('invoice.pdf', pdf.buffer); +await smartPdf.stop(); +``` + +### 🌐 From Website + +Capture any website as a PDF with two powerful methods: + +#### Standard A4 Format +Perfect for articles and documents: + +```typescript +const pdf = await smartPdf.getPdfResultForWebsite('https://example.com'); +``` + +#### Full Page Capture +Capture the entire scrollable area: + +```typescript +const fullPagePdf = await smartPdf.getFullWebsiteAsSinglePdf('https://example.com'); +``` + +### 🔀 Merge Multiple PDFs + +Combine PDFs like a pro: + +```typescript +// Load your PDFs +const invoice = await smartPdf.readFileToPdfObject('./invoice.pdf'); +const terms = await smartPdf.readFileToPdfObject('./terms.pdf'); +const contract = await smartPdf.getA4PdfResultForHtmlString('

Contract

...'); + +// Merge them in order +const mergedPdf = await smartPdf.mergePdfs([ + contract.buffer, + invoice.buffer, + terms.buffer +]); + +await fs.writeFile('complete-document.pdf', mergedPdf); +``` + +## 🖼️ Image Generation + +### 🎨 Convert PDF to Images + +SmartPDF supports three image formats, each with its own strengths: + +#### PNG - Crystal Clear Quality + +```typescript +const pngImages = await smartPdf.convertPDFToPngBytes(pdf.buffer, { + scale: SmartPdf.SCALE_HIGH // 216 DPI - perfect for most uses }); -// The server will find an available port in your specified range -await smartPdf.start(); -console.log(`Server running on port: ${smartPdf.serverPort}`); +// Save each page +pngImages.forEach((png, index) => { + fs.writeFileSync(`page-${index + 1}.png`, png); +}); ``` -### Creating PDFs from HTML Strings -Generate PDFs from HTML content with full CSS support: +#### WebP - Modern & Efficient ```typescript -async function createPdfFromHtml() { - const smartPdf = await SmartPdf.create(); +const webpImages = await smartPdf.convertPDFToWebpBytes(pdf.buffer, { + quality: 90, // 0-100 quality scale + scale: 2.0 // 144 DPI - great for web +}); +``` + +#### JPEG - Progressive Loading + +```typescript +const jpegImages = await smartPdf.convertPDFToJpegBytes(pdf.buffer, { + quality: 85, // Balance between size and quality + scale: SmartPdf.SCALE_SCREEN, // 144 DPI + maxWidth: 1920 // Constrain dimensions +}); +``` + +### 📏 DPI & Scale Guide + +SmartPDF makes it easy to get the right resolution: + +```typescript +// Built-in scale constants +SmartPdf.SCALE_SCREEN // 2.0 = ~144 DPI (web display) +SmartPdf.SCALE_HIGH // 3.0 = ~216 DPI (high quality, default) +SmartPdf.SCALE_PRINT // 6.0 = ~432 DPI (print quality) + +// Or calculate your own +const scale = SmartPdf.getScaleForDPI(300); // Get scale for 300 DPI +``` + +### 🖼️ Thumbnail Generation + +Create perfect thumbnails for document previews: + +```typescript +const thumbnails = await smartPdf.convertPDFToWebpBytes(pdf.buffer, { + scale: 0.5, // Small but readable + quality: 70, // Lower quality for tiny files + maxWidth: 200, // Constrain to thumbnail size + maxHeight: 200 +}); +``` + +## 📊 Format Comparison + +Choose the right format for your needs: + +| Format | File Size | Best For | Special Features | +|--------|-----------|----------|------------------| +| **PNG** | Largest | Screenshots, diagrams, text | Lossless, transparency | +| **JPEG** | 30-50% of PNG | Photos, complex images | Progressive loading | +| **WebP** | 25-40% of PNG | Modern web apps | Best compression | + +## 🛡️ Best Practices + +### 1. Always Use Try-Finally + +```typescript +let smartPdf: SmartPdf; + +try { + smartPdf = await SmartPdf.create(); await smartPdf.start(); - const htmlString = ` - - - - - - -

Professional PDF Document

-

This PDF was generated from HTML content.

- - - `; + // Your PDF operations - const pdf: IPdf = await smartPdf.getA4PdfResultForHtmlString(htmlString); - - // pdf.buffer contains the PDF data - // pdf.id contains a unique identifier - // pdf.name contains the filename - // pdf.metadata contains additional information like extracted text - - await smartPdf.stop(); -} -``` - -### Generating PDFs from Websites -Capture web pages as PDFs with two different approaches: - -#### A4 Format PDF from Website -Captures the viewable area formatted for A4 paper: - -```typescript -async function createA4PdfFromWebsite() { - const smartPdf = await SmartPdf.create(); - await smartPdf.start(); - - const pdf: IPdf = await smartPdf.getPdfResultForWebsite('https://example.com'); - - // Save to file - await fs.writeFile('website-a4.pdf', pdf.buffer); - - await smartPdf.stop(); -} -``` - -#### Full Webpage as Single PDF -Captures the entire webpage in a single PDF, regardless of length: - -```typescript -async function createFullPdfFromWebsite() { - const smartPdf = await SmartPdf.create(); - await smartPdf.start(); - - const pdf: IPdf = await smartPdf.getFullWebsiteAsSinglePdf('https://example.com'); - - // This captures the entire scrollable area - await fs.writeFile('website-full.pdf', pdf.buffer); - - await smartPdf.stop(); -} -``` - -### Merging Multiple PDFs -Combine multiple PDF files into a single document: - -```typescript -async function mergePdfs() { - const smartPdf = await SmartPdf.create(); - await smartPdf.start(); - - // Create or load your PDFs - const pdf1 = await smartPdf.getA4PdfResultForHtmlString('

Document 1

'); - const pdf2 = await smartPdf.getA4PdfResultForHtmlString('

Document 2

'); - const pdf3 = await smartPdf.readFileToPdfObject('./existing-document.pdf'); - - // Merge PDFs - order matters! - const mergedPdf: Uint8Array = await smartPdf.mergePdfs([ - pdf1.buffer, - pdf2.buffer, - pdf3.buffer - ]); - - // Save the merged PDF - await fs.writeFile('merged-document.pdf', mergedPdf); - - await smartPdf.stop(); -} -``` - -### Reading PDFs and Extracting Text -Extract text content from existing PDFs: - -```typescript -async function extractTextFromPdf() { - const smartPdf = await SmartPdf.create(); - - // Read PDF from disk - const pdf: IPdf = await smartPdf.readFileToPdfObject('/path/to/document.pdf'); - - // Extract all text - const extractedText = await smartPdf.extractTextFromPdfBuffer(pdf.buffer); - console.log('Extracted text:', extractedText); - - // The pdf object also contains metadata with text extraction - console.log('Metadata:', pdf.metadata); -} -``` - -### Converting PDF to PNG Images -Convert each page of a PDF into PNG images with configurable quality: - -```typescript -async function convertPdfToPng() { - const smartPdf = await SmartPdf.create(); - await smartPdf.start(); - - // Load a PDF - const pdf = await smartPdf.readFileToPdfObject('./document.pdf'); - - // Convert to PNG images with default high quality (216 DPI) - const pngImages: Uint8Array[] = await smartPdf.convertPDFToPngBytes(pdf.buffer); - - // Or specify custom scale/DPI - const highResPngs = await smartPdf.convertPDFToPngBytes(pdf.buffer, { - scale: SmartPdf.SCALE_PRINT, // 6.0 scale = ~432 DPI - maxWidth: 3000, // Optional: limit maximum width - maxHeight: 4000 // Optional: limit maximum height - }); - - // Save each page as a PNG - pngImages.forEach((pngBuffer, index) => { - fs.writeFileSync(`page-${index + 1}.png`, pngBuffer); - }); - - await smartPdf.stop(); -} -``` - -#### Understanding Scale and DPI -PDF.js renders at 72 DPI by default. Use these scale factors for different quality levels: -- `SmartPdf.SCALE_SCREEN` (2.0): ~144 DPI - Good for screen display -- `SmartPdf.SCALE_HIGH` (3.0): ~216 DPI - High quality (default) -- `SmartPdf.SCALE_PRINT` (6.0): ~432 DPI - Print quality -- Custom DPI: `scale = SmartPdf.getScaleForDPI(300)` for 300 DPI - -### Converting PDF to WebP Images -Generate web-optimized images using WebP format. WebP provides 25-35% better compression than PNG/JPEG while maintaining quality: - -```typescript -async function createWebPImages() { - const smartPdf = await SmartPdf.create(); - await smartPdf.start(); - - // Load a PDF - const pdf = await smartPdf.readFileToPdfObject('./document.pdf'); - - // Create high-quality WebP images (default: 3.0 scale = 216 DPI, 85% quality) - const webpImages = await smartPdf.convertPDFToWebpBytes(pdf.buffer); - - // Save WebP images - webpImages.forEach((webpBuffer, index) => { - fs.writeFileSync(`page-${index + 1}.webp`, webpBuffer); - }); - - await smartPdf.stop(); -} -``` - -#### Creating Thumbnails -Generate small thumbnail images for PDF galleries or document lists: - -```typescript -async function createThumbnails() { - const smartPdf = await SmartPdf.create(); - await smartPdf.start(); - - const pdf = await smartPdf.readFileToPdfObject('./document.pdf'); - - // Create small thumbnails (0.5 scale = ~36 DPI, 70% quality) - const thumbnails = await smartPdf.convertPDFToWebpBytes(pdf.buffer, { - scale: 0.5, // Small readable thumbnails - quality: 70 // Lower quality for smaller files - }); - - // Save thumbnails - thumbnails.forEach((thumb, index) => { - fs.writeFileSync(`thumb-${index + 1}.webp`, thumb); - }); - - await smartPdf.stop(); -} -``` - -#### Constrained Dimensions -Create previews with maximum width/height constraints, useful for responsive layouts: - -```typescript -async function createConstrainedPreviews() { - const smartPdf = await SmartPdf.create(); - await smartPdf.start(); - - const pdf = await smartPdf.readFileToPdfObject('./document.pdf'); - - // Create previews that fit within 800x600 pixels - const previews = await smartPdf.convertPDFToWebpBytes(pdf.buffer, { - scale: 1.0, // Start with full size - quality: 90, // High quality - maxWidth: 800, // Maximum 800px wide - maxHeight: 600 // Maximum 600px tall - }); - - // The method automatically scales down to fit within constraints - previews.forEach((preview, index) => { - fs.writeFileSync(`preview-constrained-${index + 1}.webp`, preview); - }); - - await smartPdf.stop(); -} -``` - -#### WebP Options -The `convertPDFToWebpBytes` method accepts these options: - -- `scale`: Scale factor for preview size (default: 3.0 for ~216 DPI) -- `quality`: WebP compression quality (default: 85, range: 0-100) -- `maxWidth`: Maximum width in pixels (optional) -- `maxHeight`: Maximum height in pixels (optional) - -Common scale values: -- `0.5`: Thumbnails (~36 DPI) -- `2.0`: Screen display (~144 DPI) -- `3.0`: High quality (~216 DPI, default) -- `6.0`: Print quality (~432 DPI) - -### Using External Browser Instance -For advanced use cases, you can provide your own Puppeteer browser instance: - -```typescript -import puppeteer from 'puppeteer'; - -async function useExternalBrowser() { - // Create your own browser instance with custom options - const browser = await puppeteer.launch({ - headless: true, - args: ['--no-sandbox', '--disable-setuid-sandbox'] - }); - - const smartPdf = await SmartPdf.create(); - await smartPdf.start(browser); - - // Use SmartPdf normally - const pdf = await smartPdf.getA4PdfResultForHtmlString('

Hello

'); - - // SmartPdf will not close the browser when stopping - await smartPdf.stop(); - - // You control the browser lifecycle - await browser.close(); -} -``` - -### Running Multiple Instances -Thanks to automatic port allocation, you can run multiple SmartPdf instances simultaneously: - -```typescript -async function runMultipleInstances() { - // Each instance automatically finds its own free port - const instance1 = await SmartPdf.create(); - const instance2 = await SmartPdf.create(); - const instance3 = await SmartPdf.create(); - - // Start all instances - await Promise.all([ - instance1.start(), - instance2.start(), - instance3.start() - ]); - - console.log(`Instance 1 running on port: ${instance1.serverPort}`); - console.log(`Instance 2 running on port: ${instance2.serverPort}`); - console.log(`Instance 3 running on port: ${instance3.serverPort}`); - - // Use instances independently - const pdfs = await Promise.all([ - instance1.getA4PdfResultForHtmlString('

PDF 1

'), - instance2.getA4PdfResultForHtmlString('

PDF 2

'), - instance3.getA4PdfResultForHtmlString('

PDF 3

') - ]); - - // Clean up all instances - await Promise.all([ - instance1.stop(), - instance2.stop(), - instance3.stop() - ]); -} -``` - -### Error Handling -Always wrap SmartPdf operations in try-catch blocks and ensure proper cleanup: - -```typescript -async function safePdfGeneration() { - let smartPdf: SmartPdf; - - try { - smartPdf = await SmartPdf.create(); - await smartPdf.start(); - - const pdf = await smartPdf.getA4PdfResultForHtmlString('

Hello

'); - // Process PDF... - - } catch (error) { - console.error('PDF generation failed:', error); - // Handle error appropriately - } finally { - // Always cleanup - if (smartPdf) { - await smartPdf.stop(); - } +} finally { + if (smartPdf) { + await smartPdf.stop(); // Always cleanup! } } ``` -### IPdf Interface -The `IPdf` interface represents a PDF with its metadata: +### 2. Optimize HTML for PDFs + +```typescript +const optimizedHtml = ` + + ${yourContent} +`; +``` + +### 3. Handle Large Documents + +For documents with many pages: + +```typescript +// Process in batches +const pages = await smartPdf.convertPDFToPngBytes(largePdf.buffer); + +for (let i = 0; i < pages.length; i += 10) { + const batch = pages.slice(i, i + 10); + await processBatch(batch); +} +``` + +## 🎯 Advanced Usage + +### 🌐 Custom Browser Instance + +Bring your own Puppeteer instance: + +```typescript +import puppeteer from 'puppeteer'; + +const browser = await puppeteer.launch({ + headless: 'new', + args: ['--no-sandbox', '--disable-dev-shm-usage'] +}); + +const smartPdf = await SmartPdf.create(); +await smartPdf.start(browser); + +// SmartPdf won't close your browser +await smartPdf.stop(); +await browser.close(); // You manage it +``` + +### ⚡ Parallel Processing + +Process multiple PDFs concurrently: + +```typescript +const urls = ['https://example1.com', 'https://example2.com', 'https://example3.com']; + +const pdfs = await Promise.all( + urls.map(url => smartPdf.getFullWebsiteAsSinglePdf(url)) +); + +// Or with multiple instances for maximum performance +const instances = await Promise.all( + Array(3).fill(null).map(() => SmartPdf.create()) +); + +await Promise.all(instances.map(i => i.start())); + +// Process in parallel across instances +const results = await Promise.all( + urls.map((url, i) => instances[i % instances.length].getFullWebsiteAsSinglePdf(url)) +); + +// Cleanup all instances +await Promise.all(instances.map(i => i.stop())); +``` + +## 📝 API Reference + +### Class: SmartPdf + +#### Static Methods +- `create(options?: ISmartPdfOptions)` - Create a new SmartPdf instance +- `getScaleForDPI(dpi: number)` - Calculate scale factor for desired DPI + +#### Instance Methods +- `start(browser?: Browser)` - Start the PDF server +- `stop()` - Stop the PDF server +- `getA4PdfResultForHtmlString(html: string)` - Generate A4 PDF from HTML +- `getPdfResultForWebsite(url: string)` - Generate A4 PDF from website +- `getFullWebsiteAsSinglePdf(url: string)` - Capture full webpage as PDF +- `mergePdfs(buffers: Uint8Array[])` - Merge multiple PDFs +- `readFileToPdfObject(path: string)` - Read PDF file from disk +- `extractTextFromPdfBuffer(buffer: Buffer)` - Extract text from PDF +- `convertPDFToPngBytes(buffer: Uint8Array, options?)` - Convert to PNG +- `convertPDFToWebpBytes(buffer: Uint8Array, options?)` - Convert to WebP +- `convertPDFToJpegBytes(buffer: Uint8Array, options?)` - Convert to JPEG + +### Interface: IPdf ```typescript interface IPdf { - name: string; // Filename of the PDF - buffer: Buffer; // PDF content as buffer - id: string | null; // Unique identifier + name: string; // Filename + buffer: Buffer; // PDF content + id: string | null; // Unique identifier metadata?: { - textExtraction?: string; // Extracted text content + textExtraction?: string; // Extracted text }; } ``` -## Best Practices +## 🤝 Contributing -1. **Always start and stop**: Initialize with `start()` and cleanup with `stop()` to properly manage resources. -2. **Port management**: Use the automatic port allocation feature to avoid conflicts when running multiple instances. -3. **Error handling**: Always implement proper error handling as PDF generation can fail due to various reasons. -4. **Resource cleanup**: Ensure `stop()` is called even if an error occurs to prevent memory leaks. -5. **HTML optimization**: When creating PDFs from HTML, ensure your HTML is well-formed and CSS is embedded or inlined. +We love contributions! Please feel free to submit a Pull Request. ## License and Legal Information diff --git a/test/test.ts b/test/test.ts index 0115d5b..333727d 100644 --- a/test/test.ts +++ b/test/test.ts @@ -174,6 +174,113 @@ tap.test('should verify WebP files are smaller than PNG', async () => { expect(totalWebpSize).toBeLessThan(totalPngSize); }); +tap.test('should create JPEG images from PDF', async () => { + const pdfObject = await testSmartPdf.readFileToPdfObject('.nogit/3.pdf'); + const jpegImages = await testSmartPdf.convertPDFToJpegBytes(pdfObject.buffer); + expect(jpegImages.length).toBeGreaterThan(0); + console.log('JPEG image sizes:', jpegImages.map(img => img.length)); + + // Save the first page as JPEG + fs.writeFileSync(path.join(testResultsDir, 'jpeg_default_page1.jpg'), Buffer.from(jpegImages[0])); +}); + +tap.test('should create JPEG images with different quality levels', async () => { + const pdfObject = await testSmartPdf.readFileToPdfObject('.nogit/3.pdf'); + + // Test different quality levels + const qualityLevels = [50, 70, 85, 95]; + + for (const quality of qualityLevels) { + const jpegImages = await testSmartPdf.convertPDFToJpegBytes(pdfObject.buffer, { + scale: smartpdf.SmartPdf.SCALE_HIGH, + quality: quality + }); + + console.log(`JPEG quality ${quality}: ${jpegImages[0].length} bytes`); + + // Save first page at each quality level + fs.writeFileSync( + path.join(testResultsDir, `jpeg_quality_${quality}_page1.jpg`), + Buffer.from(jpegImages[0]) + ); + } +}); + +tap.test('should create JPEG images with max dimensions', async () => { + const pdfObject = await testSmartPdf.readFileToPdfObject('.nogit/3.pdf'); + + // Create constrained JPEG images + const constrainedJpegs = await testSmartPdf.convertPDFToJpegBytes(pdfObject.buffer, { + scale: smartpdf.SmartPdf.SCALE_HIGH, + quality: 85, + maxWidth: 1200, + maxHeight: 1200 + }); + + expect(constrainedJpegs.length).toBeGreaterThan(0); + console.log('Constrained JPEG sizes:', constrainedJpegs.map(img => img.length)); + + // Save constrained JPEG + fs.writeFileSync(path.join(testResultsDir, 'jpeg_constrained_page1.jpg'), Buffer.from(constrainedJpegs[0])); +}); + +tap.test('should compare file sizes between PNG, WebP, and JPEG', async () => { + const pdfObject = await testSmartPdf.readFileToPdfObject('.nogit/3.pdf'); + + // Generate all three formats at the same scale + const comparisonScale = smartpdf.SmartPdf.SCALE_HIGH; // 3.0 scale + + const pngImages = await testSmartPdf.convertPDFToPngBytes(pdfObject.buffer, { + scale: comparisonScale + }); + const webpImages = await testSmartPdf.convertPDFToWebpBytes(pdfObject.buffer, { + scale: comparisonScale, + quality: 85 + }); + const jpegImages = await testSmartPdf.convertPDFToJpegBytes(pdfObject.buffer, { + scale: comparisonScale, + quality: 85 + }); + + expect(pngImages.length).toEqual(webpImages.length); + expect(pngImages.length).toEqual(jpegImages.length); + + // Compare sizes + let totalPngSize = 0; + let totalWebpSize = 0; + let totalJpegSize = 0; + + pngImages.forEach((png, index) => { + const pngSize = png.length; + const webpSize = webpImages[index].length; + const jpegSize = jpegImages[index].length; + + totalPngSize += pngSize; + totalWebpSize += webpSize; + totalJpegSize += jpegSize; + + const webpReduction = ((pngSize - webpSize) / pngSize * 100).toFixed(1); + const jpegReduction = ((pngSize - jpegSize) / pngSize * 100).toFixed(1); + + console.log(`Page ${index + 1}:`); + console.log(` PNG: ${pngSize} bytes`); + console.log(` WebP: ${webpSize} bytes (${webpReduction}% smaller than PNG)`); + console.log(` JPEG: ${jpegSize} bytes (${jpegReduction}% smaller than PNG)`); + }); + + const totalWebpReduction = ((totalPngSize - totalWebpSize) / totalPngSize * 100).toFixed(1); + const totalJpegReduction = ((totalPngSize - totalJpegSize) / totalPngSize * 100).toFixed(1); + + console.log('\nTotal size comparison:'); + console.log(`PNG: ${totalPngSize} bytes`); + console.log(`WebP: ${totalWebpSize} bytes (${totalWebpReduction}% reduction)`); + console.log(`JPEG: ${totalJpegSize} bytes (${totalJpegReduction}% reduction)`); + + // JPEG and WebP should both be smaller than PNG + expect(totalJpegSize).toBeLessThan(totalPngSize); + expect(totalWebpSize).toBeLessThan(totalPngSize); +}); + tap.test('should close the SmartPdf instance properly', async () => { await testSmartPdf.stop(); }); diff --git a/ts/smartpdf.classes.smartpdf.ts b/ts/smartpdf.classes.smartpdf.ts index 0bc397c..b2c0886 100644 --- a/ts/smartpdf.classes.smartpdf.ts +++ b/ts/smartpdf.classes.smartpdf.ts @@ -538,4 +538,52 @@ export class SmartPdf { await page.close(); return webpBuffers; } + + /** + * Converts a PDF to progressive JPEG bytes for each page. + * This method creates progressive JPEG images that load in multiple passes, + * showing a low-quality preview first, then progressively improving. + * Uses SmartJimp for true progressive JPEG encoding. + */ + public async convertPDFToJpegBytes( + pdfBytes: Uint8Array, + options: { + scale?: number; // Scale factor for output size (default: 3.0 for 216 DPI) + quality?: number; // JPEG quality 0-100 (default: 85) + maxWidth?: number; // Maximum width in pixels (optional) + maxHeight?: number; // Maximum height in pixels (optional) + } = {} + ): Promise { + // First, convert PDF to PNG using our existing method + const pngBuffers = await this.convertPDFToPngBytes(pdfBytes, { + scale: options.scale, + maxWidth: options.maxWidth, + maxHeight: options.maxHeight + }); + + // Initialize SmartJimp in sharp mode for progressive JPEG support + const smartJimpInstance = new plugins.smartjimp.SmartJimp({ mode: 'sharp' }); + + // Convert each PNG to progressive JPEG + const jpegBuffers: Uint8Array[] = []; + const quality = options.quality || 85; + + for (const pngBuffer of pngBuffers) { + // Convert PNG buffer to progressive JPEG + const jpegBuffer = await smartJimpInstance.computeAssetVariation( + Buffer.from(pngBuffer), + { + format: 'jpeg', + progressive: true, + // SmartJimp uses a different quality scale, need to check if adjustment is needed + // For now, pass through the quality value + quality + } + ); + + jpegBuffers.push(new Uint8Array(jpegBuffer)); + } + + return jpegBuffers; + } } \ No newline at end of file diff --git a/ts/smartpdf.plugins.ts b/ts/smartpdf.plugins.ts index df2639c..802a381 100644 --- a/ts/smartpdf.plugins.ts +++ b/ts/smartpdf.plugins.ts @@ -13,6 +13,7 @@ import * as smartpath from '@push.rocks/smartpath'; import * as smartpuppeteer from '@push.rocks/smartpuppeteer'; import * as smartnetwork from '@push.rocks/smartnetwork'; import * as smartunique from '@push.rocks/smartunique'; +import * as smartjimp from '@push.rocks/smartjimp'; export { smartbuffer, @@ -23,6 +24,7 @@ export { smartpuppeteer, smartunique, smartnetwork, + smartjimp, }; // tsclass scope