diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 655d659..9d5c088 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,7 +18,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@v6 + - uses: actions/stale@v7 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'Stale issue message' diff --git a/PdfjsSharp/IRasterizer.cs b/PdfjsSharp/IRasterizer.cs index 247f324..f691b8e 100644 --- a/PdfjsSharp/IRasterizer.cs +++ b/PdfjsSharp/IRasterizer.cs @@ -4,16 +4,16 @@ namespace Codeuctivity.PdfjsSharp { /// - /// Rasterizes PDFs, Depands on node + /// Rasterizes PDFs, Depends on node /// public interface IRasterizer { /// - /// Converts a pdf to pngs + /// Converts a PDF to PNGs /// /// - /// Prefix of file path to pngs created for each page of pdf. E.g. c:\temp\PdfPage will result in c:\temp\PdfPage1.png and c:\temp\PdfPage2.png for a Pdf with two pages. - /// Collection of paths to pngs for each page in the pdf + /// Prefix of file path to PNGs created for each page of pdf. E.g. c:\temp\PdfPage will result in c:\temp\PdfPage1.PNG and c:\temp\PdfPage2.PNG for a Pdf with two pages. + /// Collection of paths to PNGs for each page in the pdf Task> ConvertToPngAsync(string pathToPdf, string pathToPngOutput); } } \ No newline at end of file diff --git a/PdfjsSharp/NodeVersionDetector.cs b/PdfjsSharp/NodeVersionDetector.cs index cbbeda1..b6140d0 100644 --- a/PdfjsSharp/NodeVersionDetector.cs +++ b/PdfjsSharp/NodeVersionDetector.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; @@ -16,11 +17,11 @@ public static class NodeVersionDetector /// /// Reads installed node version /// - public static Version? DetectVersion() + public static Version? DetectVersion(string nodeExecuteablePath) { using var process = new Process(); process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.FileName = "node"; + process.StartInfo.FileName = nodeExecuteablePath; process.StartInfo.Arguments = "-v"; process.Start(); process.WaitForExit(); @@ -43,11 +44,11 @@ public static class NodeVersionDetector /// /// Reads installed node bitness version /// - public static string DetectBittness() + public static string DetectBittness(string nodeExecuteablePath) { using var process = new Process(); process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.FileName = "node"; + process.StartInfo.FileName = nodeExecuteablePath; process.StartInfo.Arguments = "-p \"process.arch\""; process.Start(); process.WaitForExit(); @@ -63,17 +64,18 @@ public static string DetectBittness() /// /// Check that node with majorNodeVersion is installed /// + /// /// - public static int CheckRequiredNodeVersionInstalled(int[] supportedMajorNodeVersions) + public static int CheckRequiredNodeVersionInstalled(string nodeExecuteablePath, IEnumerable supportedMajorNodeVersions) { - var foundMajorVersion = (DetectVersion())?.Major; + var foundMajorVersion = (DetectVersion(nodeExecuteablePath))?.Major; if (foundMajorVersion == null) { throw new NotSupportedException($"No supported node version found. Expected node {supportedMajorNodeVersions} to be installed."); } - if (DetectBittness() != "x64") + if (DetectBittness(nodeExecuteablePath) != "x64") { throw new NotSupportedException($"No supported node version found. Expected 64bit node to be installed."); } diff --git a/PdfjsSharp/PdfJsWrapper.cs b/PdfjsSharp/PdfJsWrapper.cs index de2d5de..af745e9 100644 --- a/PdfjsSharp/PdfJsWrapper.cs +++ b/PdfjsSharp/PdfJsWrapper.cs @@ -1,6 +1,10 @@ +using Jering.Javascript.NodeJS; using System; +using System.Collections.Immutable; +using System.Diagnostics; using System.IO; using System.IO.Compression; +using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; @@ -9,19 +13,46 @@ namespace Codeuctivity.PdfjsSharp { /// - /// Wraps pdfjs boilerplaiting + /// Wraps PDFjs boiler plaiting /// public class PdfJsWrapper : IDisposable { - internal const int someMaxPathLength = 206; + internal const int windowsMaxPathLength = 206; internal static readonly SemaphoreSlim semaphore = new SemaphoreSlim(1, 1); internal bool useCustomNodeModulePath; internal string pathToNodeModules = default!; internal string pathToTempFolder = default!; private bool disposed; + /// + /// Supported node versions + /// + private readonly ImmutableArray SupportedNodeVersions = ImmutableArray.Create(new[] { 18, 16 }); + internal bool IsInitialized { get; set; } + /// + /// Path to node executable + /// + public string NodeExecuteablePath { get; private set; } + + /// + /// Ctor used with custom NodeExecuteablePath + /// + /// + public PdfJsWrapper(string nodeExecuteablePath) + { + NodeExecuteablePath = nodeExecuteablePath; + } + + /// + /// Default ctor, NodeExecuteablePath gets auto detected + /// + public PdfJsWrapper() + { + NodeExecuteablePath = "node"; + } + /// /// Disposing packaged node_modules folder /// @@ -55,25 +86,38 @@ protected virtual void Dispose(bool disposing) disposed = true; } - internal async Task InitNodeModules() + /// + /// Needs to be called before any method can be used + /// + /// + /// + public async Task InitPdfJsWrapper() { + if (IsInitialized) + { + return; + } + await semaphore.WaitAsync().ConfigureAwait(false); + + if (IsInitialized) + { + return; + } + try { - if (IsInitialized) - { - return; - } + InitializeNodeExecuteablePath(); pathToTempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (pathToTempFolder.Length > someMaxPathLength) + if (pathToTempFolder.Length > windowsMaxPathLength) { throw new PathTooLongException(pathToTempFolder); } - var foundVersion = NodeVersionDetector.CheckRequiredNodeVersionInstalled(new[] { 18, 16 }); + var foundVersion = NodeVersionDetector.CheckRequiredNodeVersionInstalled(NodeExecuteablePath, SupportedNodeVersions); Directory.CreateDirectory(pathToTempFolder); @@ -83,7 +127,7 @@ internal async Task InitNodeModules() } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - var foundVersion = NodeVersionDetector.CheckRequiredNodeVersionInstalled(new[] { 18, 16 }); + var foundVersion = NodeVersionDetector.CheckRequiredNodeVersionInstalled(NodeExecuteablePath, SupportedNodeVersions); Directory.CreateDirectory(pathToTempFolder); await ExtractBinaryFromManifest($"Codeuctivity.PdfjsSharp.node_modules.linux.node{foundVersion}.zip").ConfigureAwait(false); @@ -95,6 +139,11 @@ internal async Task InitNodeModules() useCustomNodeModulePath = true; } + if (!string.IsNullOrEmpty(NodeExecuteablePath) && NodeExecuteablePath != "node") + { + StaticNodeJSService.Configure(options => options.ExecutablePath = NodeExecuteablePath); + } + IsInitialized = true; } finally @@ -103,6 +152,55 @@ internal async Task InitNodeModules() } } + private void InitializeNodeExecuteablePath() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + var filePathWhich = "/usr/bin/which"; + + if (File.Exists(filePathWhich)) + { + using var process = new Process(); + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.FileName = filePathWhich; + process.StartInfo.Arguments = "node"; + process.Start(); + process.WaitForExit(); + + var detectedPath = process.StandardOutput.ReadToEnd(); + if (!string.IsNullOrEmpty(detectedPath) && File.Exists(detectedPath)) + { + NodeExecuteablePath = detectedPath; + return; + } + + // not finding node is a WSL only issue + // known location of node installed by nvm in wsl - https://learn.microsoft.com/en-us/windows/dev-environment/javascript/nodejs-on-wsl + + var home = Environment.GetEnvironmentVariable("HOME"); + + var path = Path.Combine(home, ".nvm", "versions", "node"); + if (!string.IsNullOrEmpty(home) && Directory.Exists(path)) + { + var installedNodeVersions = Directory.GetDirectories(path); + + var nodeExecutableDirectory = installedNodeVersions.FirstOrDefault(directory => SupportedNodeVersions.Any(version => Path.GetFileName(directory).StartsWith("v" + version.ToString()))); + + if (nodeExecutableDirectory != null) + { + var nodePath = Path.Combine(path, nodeExecutableDirectory, "bin", "node"); + if (File.Exists(nodePath)) + { + NodeExecuteablePath = nodePath; + return; + } + } + } + } + } + NodeExecuteablePath = "node"; + } + private async Task ExtractBinaryFromManifest(string resourceName) { var pathNodeModules = Path.Combine(pathToTempFolder, "node_modules.zip"); diff --git a/PdfjsSharp/PdfjsSharp.csproj b/PdfjsSharp/PdfjsSharp.csproj index 6d38d0e..00ee58c 100644 --- a/PdfjsSharp/PdfjsSharp.csproj +++ b/PdfjsSharp/PdfjsSharp.csproj @@ -44,10 +44,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/PdfjsSharp/Rasterizer.cs b/PdfjsSharp/Rasterizer.cs index f79e4ce..52cc897 100644 --- a/PdfjsSharp/Rasterizer.cs +++ b/PdfjsSharp/Rasterizer.cs @@ -12,14 +12,29 @@ namespace Codeuctivity.PdfjsSharp public class Rasterizer : PdfJsWrapper, IRasterizer { /// - /// Converts a pdf to PNGs + /// Default ctor + /// + public Rasterizer() + { + } + + /// + /// Ctor used with custom NodeExecuteablePath + /// + /// + public Rasterizer(string nodeExecuteablePath) : base(nodeExecuteablePath) + { + } + + /// + /// Converts a PDF to PNGs /// /// - /// Prefix of file path to PNGs created for each page of pdf. E.g. c:\temp\PdfPage will result in c:\temp\PdfPage1.png and c:\temp\PdfPage2.png for a Pdf with two pages. - /// Collection of paths to PNGs for each page in the pdf + /// Prefix of file path to PNGs created for each page of PDF. E.g. c:\temp\PdfPage will result in c:\temp\PdfPage1.png and c:\temp\PdfPage2.png for a PDF with two pages. + /// Collection of paths to PNGs for each page in the PDF public async Task> ConvertToPngAsync(string pathToPdf, string pathToPngOutput) { - await InitNodeModules().ConfigureAwait(false); + await InitPdfJsWrapper().ConfigureAwait(false); var assembly = Assembly.GetExecutingAssembly(); var stream = assembly.GetManifestResourceStream("Codeuctivity.PdfjsSharp.Rasterize.js"); using var reader = new StreamReader(stream!); diff --git a/PdfjsSharp/package-lock.json b/PdfjsSharp/package-lock.json index a6d37bc..994b7f7 100644 --- a/PdfjsSharp/package-lock.json +++ b/PdfjsSharp/package-lock.json @@ -10,8 +10,8 @@ "license": "Apache-2.0", "dependencies": { "assert": "^2.0.0", - "canvas": "^2.10.2", - "eslint": "^8.29.0", + "canvas": "^2.11.0", + "eslint": "^8.30.0", "pdfjs-dist": "^3.1.81" }, "devDependencies": { @@ -19,14 +19,14 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", + "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.4.0", - "globals": "^13.15.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -41,9 +41,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -125,6 +125,7 @@ "version": "2.10.378", "resolved": "https://registry.npmjs.org/@types/pdfjs-dist/-/pdfjs-dist-2.10.378.tgz", "integrity": "sha512-TRdIPqdsvKmPla44kVy4jv5Nt5vjMfVjbIEke1CRULIrwKNRC4lIiZvNYDJvbUMNCFPNIUcOKhXTyMJrX18IMA==", + "deprecated": "This is a stub types definition. pdfjs-dist provides its own type definitions, so you do not need this installed.", "dev": true, "dependencies": { "pdfjs-dist": "*" @@ -271,9 +272,9 @@ } }, "node_modules/canvas": { - "version": "2.10.2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.10.2.tgz", - "integrity": "sha512-FSmlsip0nZ0U4Zcfht0qBJqDhlfGuevTZKE8h+dBOYrJjGvY3iqMGSzzbvkaFhvMXiVxfcMaPHS/kge++T5SKg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.0.tgz", + "integrity": "sha512-bdTjFexjKJEwtIo0oRx8eD4G2yWoUOXP9lj279jmQ2zMnTQhT8C3512OKz3s+ZOaQlLbE7TuVvRDYDB3Llyy5g==", "hasInstallScript": true, "dependencies": { "@mapbox/node-pre-gyp": "^1.0.0", @@ -469,12 +470,12 @@ } }, "node_modules/eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "version": "8.30.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz", + "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==", "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint/eslintrc": "^1.4.0", + "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -493,7 +494,7 @@ "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", + "globals": "^13.19.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", @@ -563,9 +564,9 @@ } }, "node_modules/espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", @@ -766,9 +767,9 @@ } }, "node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", "dependencies": { "type-fest": "^0.20.2" }, @@ -829,9 +830,9 @@ } }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "engines": { "node": ">= 4" } @@ -1740,14 +1741,14 @@ }, "dependencies": { "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", + "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.4.0", - "globals": "^13.15.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -1756,9 +1757,9 @@ } }, "@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "requires": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -1934,9 +1935,9 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, "canvas": { - "version": "2.10.2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.10.2.tgz", - "integrity": "sha512-FSmlsip0nZ0U4Zcfht0qBJqDhlfGuevTZKE8h+dBOYrJjGvY3iqMGSzzbvkaFhvMXiVxfcMaPHS/kge++T5SKg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.0.tgz", + "integrity": "sha512-bdTjFexjKJEwtIo0oRx8eD4G2yWoUOXP9lj279jmQ2zMnTQhT8C3512OKz3s+ZOaQlLbE7TuVvRDYDB3Llyy5g==", "requires": { "@mapbox/node-pre-gyp": "^1.0.0", "nan": "^2.17.0", @@ -2086,12 +2087,12 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "version": "8.30.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz", + "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==", "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint/eslintrc": "^1.4.0", + "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -2110,7 +2111,7 @@ "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", + "globals": "^13.19.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", @@ -2161,9 +2162,9 @@ "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" }, "espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", "requires": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", @@ -2321,9 +2322,9 @@ } }, "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", "requires": { "type-fest": "^0.20.2" } @@ -2366,9 +2367,9 @@ } }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" }, "import-fresh": { "version": "3.3.0", diff --git a/PdfjsSharp/package.json b/PdfjsSharp/package.json index 25ccb9c..3ca44ec 100644 --- a/PdfjsSharp/package.json +++ b/PdfjsSharp/package.json @@ -5,8 +5,8 @@ "main": "Rasterize.js", "dependencies": { "assert": "^2.0.0", - "canvas": "^2.10.2", - "eslint": "^8.29.0", + "canvas": "^2.11.0", + "eslint": "^8.30.0", "pdfjs-dist": "^3.1.81" }, "scripts": { diff --git a/PdfjsSharpTests/NodeVersionDetectorTest.cs b/PdfjsSharpTests/NodeVersionDetectorTest.cs index da036d9..c0d2c18 100644 --- a/PdfjsSharpTests/NodeVersionDetectorTest.cs +++ b/PdfjsSharpTests/NodeVersionDetectorTest.cs @@ -4,33 +4,33 @@ namespace PdfjsSharpTests { - public class NodeVersionDetectorTest + public class NodeVersionDetectorTest : NodeVersionDetectorTestFixture { [Fact] public void ShouldDetectNodeVersionAsync() { - var actualInstalledVersion = NodeVersionDetector.DetectVersion(); + var actualInstalledVersion = NodeVersionDetector.DetectVersion(Rasterizer.NodeExecuteablePath); Assert.True(actualInstalledVersion.Major > 6); } [Fact] - public void ShouldSuccescullyDetermineInstalledVersionAsync() + public void ShouldDetermineInstalledVersionAsync() { - var actualInstalledVersion = NodeVersionDetector.CheckRequiredNodeVersionInstalled(new[] { 8, 16, 18 }); + var actualInstalledVersion = NodeVersionDetector.CheckRequiredNodeVersionInstalled(Rasterizer.NodeExecuteablePath, new[] { 8, 16, 18 }); Assert.True(actualInstalledVersion > 6); } [Fact] public void ShouldThrowOnMissingNodeVersion() { - var exception = Assert.Throws(() => NodeVersionDetector.CheckRequiredNodeVersionInstalled(new[] { 9999 })); + var exception = Assert.Throws(() => NodeVersionDetector.CheckRequiredNodeVersionInstalled(Rasterizer.NodeExecuteablePath, new[] { 9999 })); Assert.Contains(" Expected a supported node version 9999 to be installed.", exception.Message); } [Fact] public void ShouldDetectNodeBittnes() { - var detectedBittnes = NodeVersionDetector.DetectBittness(); + var detectedBittnes = NodeVersionDetector.DetectBittness(Rasterizer.NodeExecuteablePath); Assert.Equal("x64", detectedBittnes); } } diff --git a/PdfjsSharpTests/NodeVersionDetectorTestFixture.cs b/PdfjsSharpTests/NodeVersionDetectorTestFixture.cs new file mode 100644 index 0000000..0062b15 --- /dev/null +++ b/PdfjsSharpTests/NodeVersionDetectorTestFixture.cs @@ -0,0 +1,27 @@ +using Codeuctivity.PdfjsSharp; +using System.Threading.Tasks; +using Xunit; + +namespace PdfjsSharpTests +{ + public class NodeVersionDetectorTestFixture : IAsyncLifetime + { + public Rasterizer Rasterizer { get; } + + public NodeVersionDetectorTestFixture() + { + Rasterizer = new Rasterizer(); + } + + public Task InitializeAsync() + { + return Rasterizer.InitPdfJsWrapper(); + } + + public Task DisposeAsync() + { + Rasterizer.Dispose(); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/PdfjsSharpTests/PdfjsSharpTests.csproj b/PdfjsSharpTests/PdfjsSharpTests.csproj index 7623732..ca028d0 100644 --- a/PdfjsSharpTests/PdfjsSharpTests.csproj +++ b/PdfjsSharpTests/PdfjsSharpTests.csproj @@ -8,8 +8,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/PdfjsSharpTests/RasterizerTests.cs b/PdfjsSharpTests/RasterizerTests.cs index a4dad14..e218ea5 100644 --- a/PdfjsSharpTests/RasterizerTests.cs +++ b/PdfjsSharpTests/RasterizerTests.cs @@ -7,15 +7,27 @@ namespace PdfjsSharpTests { - public class RasterizerTests + public class RasterizerTests : NodeVersionDetectorTestFixture { [Fact] public async Task ShouldCreatePngFromPdf() { var actualImagePath = Path.Combine(Path.GetTempPath(), "ActualShouldCreatePngFromPdf"); - using var rasterizer = new Rasterizer(); + var actualImages = await Rasterizer.ConvertToPngAsync(@"../../../SourceTest.pdf", actualImagePath); - var actualImages = await rasterizer.ConvertToPngAsync(@"../../../SourceTest.pdf", actualImagePath); + Assert.Equal(1, actualImages.Count); + Assert.True(File.Exists(actualImages.Single()), "Actual output file not found"); + Assert.True(ImageSharpCompare.ImagesAreEqual(actualImages.Single(), @"../../../ExpectedImages/ExpectedShouldCreatePngFromPdf1.png"), "Actual and expected image do differ"); + File.Delete(actualImages.Single()); + } + + [Fact] + public async Task ShouldCreatePngFromPdfCustomNodeExecutablePath() + { + var actualImagePath = Path.Combine(Path.GetTempPath(), "ActualShouldCreatePngFromPdfCustomNodeExecutablePath"); + using var rasterizerSut = new Rasterizer(Rasterizer.NodeExecuteablePath); + + var actualImages = await rasterizerSut.ConvertToPngAsync(@"../../../SourceTest.pdf", actualImagePath); Assert.Equal(1, actualImages.Count); Assert.True(File.Exists(actualImages.Single()), "Actual output file not found"); diff --git a/README.md b/README.md index 2af3661..f3403a5 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # PDFjsSharp -[![Nuget](https://img.shields.io/nuget/v/Codeuctivity.PdfjsSharp.svg)](https://www.nuget.org/packages/Codeuctivity.PdfjsSharp/) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/c417a8e923da45ed90c302c4a23528ea)](https://www.codacy.com/gh/Codeuctivity/PdfjsSharp?utm_source=github.com&utm_medium=referral&utm_content=Codeuctivity/PdfjsSharp&utm_campaign=Badge_Grade) [![Build](https://github.com/Codeuctivity/PdfjsSharp/actions/workflows/dotnet.yml/badge.svg)](https://github.com/Codeuctivity/PdfjsSharp/actions/workflows/dotnet.yml) [![Donate](https://img.shields.io/static/v1?label=Paypal&message=Donate&color=informational)](https://www.paypal.com/donate?hosted_button_id=7M7UFMMRTS7UE) +[![Nuget](https://img.shields.io/nuget/v/Codeuctivity.PdfjsSharp.svg)](https://www.nuget.org/packages/Codeuctivity.PdfjsSharp/) [![Build](https://github.com/Codeuctivity/PdfjsSharp/actions/workflows/dotnet.yml/badge.svg)](https://github.com/Codeuctivity/PdfjsSharp/actions/workflows/dotnet.yml) [![Donate](https://img.shields.io/static/v1?label=Paypal&message=Donate&color=informational)](https://www.paypal.com/donate?hosted_button_id=7M7UFMMRTS7UE) Brings Pdfjs to .net ## Feature -- Renders each page of a pdf to pngs +- Renders each page of a PDF to PNGs ## Dependencies @@ -17,7 +17,7 @@ get - .NET Framework 4.6.1 or .NET Core 2.0 or [something newer](https://github.com/dotnet/standard/blob/master/docs/versions/netstandard2.0.md) -## Howto use +## HowTo use - Install nuget package [PdfjsSharp](https://www.nuget.org/packages/Codeuctivity.PdfjsSharp/) @@ -33,10 +33,10 @@ Assert.Equal(1, actualImages.Count); Visual Studio 2022 or .net 6 SDK -### Upgarde npm packages +### Upgrade npm packages - temporary remove 'MagicPrefix' from Rasterize.js -- npx npm-check --update-all +- npx npm-check --update-all --skip-unused - revert changes in Rasterize.js #### Steps to update node_modules.win.\*.zip @@ -47,29 +47,12 @@ Visual Studio 2022 or .net 6 SDK nvm install 16;nvm use 16;rm -R .\node_modules\;npm install --omit=dev;rm .\node_modules.win.node16.zip;Compress-Archive -LiteralPath .\node_modules\ -DestinationPath .\node_modules.win.node16.zip ``` -### Ubuntu 20.04 +### Ubuntu 22.04 ```bash #In case you have no nvm -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && exit -# In case of using wsl: you need to set environment variables - -#In case you have .net sdk not setup -export DOTNET_CLI_TELEMETRY_OPTOUT=1 -sudo snap remove dotnet-sdk -wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb -sudo dpkg -i packages-microsoft-prod.deb -sudo apt update -sudo apt install apt-transport-https zip -sudo apt update -sudo apt install dotnet-sdk-6.0 zip -y -echo export DOTNET_CLI_TELEMETRY_OPTOUT=1>> ~/.bash_profile -nvm install 16 -``` - -### Ubuntu 22.04 +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash -```bash sudo snap remove dotnet-sdk sudo apt remove 'dotnet*' sudo apt remove 'aspnetcore*' @@ -78,6 +61,7 @@ sudo rm /etc/apt/sources.list.d/microsoft-prod.list sudo rm /etc/apt/sources.list.d/microsoft-prod.list.save sudo apt update sudo apt install dotnet6 zip +echo export DOTNET_CLI_TELEMETRY_OPTOUT=1>> ~/.bash_profile ``` #### Steps to update node_modules.linux.node\*.zip