public static MemoryStream RenderPDFPage(string pdf_filename, int page_number, int dpi, int height, int width, string password, ProcessPriorityClass priority_class) { WPFDoEvents.AssertThisCodeIs_NOT_RunningInTheUIThread(); render_count++; string process_parameters = String.Format( $"draw -q -w {width} -h {height} -r {dpi} -o -" + " " + (String.IsNullOrEmpty(password) ? "" : "-p " + password) + " " + '"' + pdf_filename + '"' + " " + page_number ); string exe = Path.GetFullPath(Path.Combine(UnitTestDetector.StartupDirectoryForQiqqa, @"MuPDF/mutool.exe")); if (!File.Exists(exe)) { throw new Exception($"PDF Page Rendering: missing modern MuPDF 'mudraw.exe': it does not exist in the expected path: '{exe}'"); } if (!File.Exists(pdf_filename)) { throw new Exception($"PDF Page Rendering: INTERNAL ERROR: missing PDF: it does not exist in the expected path: '{pdf_filename}'"); } ExecResultAggregate rv = ReadEntireStandardOutput(exe, process_parameters, binary_output: true, priority_class); if (rv.error != null) { rv.stdoutStream?.Close(); throw rv.error; } return(rv.stdoutStream); }
private static ExecResultAggregate ReadEntireStandardOutput(string pdfDrawExe, string process_parameters, bool binary_output, ProcessPriorityClass priority_class) { WPFDoEvents.AssertThisCodeIs_NOT_RunningInTheUIThread(); ExecResultAggregate rv = new ExecResultAggregate { executable = pdfDrawExe, process_parameters = process_parameters, stdoutIsBinary = binary_output }; Stopwatch clk = Stopwatch.StartNew(); // STDOUT/STDERR Logging.Debug("PDFDRAW :: ReadEntireStandardOutput command: pdfdraw.exe {0}", process_parameters); using (Process process = ProcessSpawning.SpawnChildProcess(pdfDrawExe, process_parameters, priority_class, stdout_is_binary: true)) { using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process, stdout_is_binary: true)) { // Read image from stdout using (StreamReader sr = process.StandardOutput) { using (FileStream fs = (FileStream)sr.BaseStream) { long elapsed = clk.ElapsedMilliseconds; Logging.Debug("PDFDRAW :: ReadEntireStandardOutput setup time: {0} ms for parameters:\n {1}", elapsed, process_parameters); rv.stdoutStream = new MemoryStream(1024 * 1024); int total_size = StreamToFile.CopyStreamToStream(fs, rv.stdoutStream); long elapsed2 = clk.ElapsedMilliseconds; Logging.Debug("PDFDRAW image output {0} bytes in {1} ms (output copy took {2} ms) for command:\n {4} {3}", total_size, elapsed2, elapsed2 - elapsed, process_parameters, pdfDrawExe); // Check that the process has exited properly process.WaitForExit(1000); if (!process.HasExited) { Logging.Debug("PDFRenderer process did not terminate, so killing it.\n{0}", process_output_reader.GetOutputsDumpStrings().stderr); try { process.Kill(); // wait for the completion signal; this also helps to collect all STDERR output of the application (even while it was KILLED) process.WaitForExit(3000); } catch (Exception ex) { Logging.Error(ex, "There was a problem killing the PDFRenderer process after timeout ({0} ms)", elapsed2 + 1000); } // grab stderr output for successful runs and log it anyway: MuPDF diagnostics, etc. come this way: var outs = process_output_reader.GetOutputsDumpStrings(); rv.errOutputDump = outs; Logging.Error($"PDFRenderer process did not terminate, so killed it. Commandline:\n {pdfDrawExe} {process_parameters}\n{outs.stderr}"); rv.error = new ApplicationException($"PDFRenderer process did not terminate, so killed it.\n Commandline: {pdfDrawExe} {process_parameters}"); rv.exitCode = 0; if (process.HasExited) { rv.exitCode = process.ExitCode; } if (rv.exitCode == 0) { rv.exitCode = -666; // timeout } } else if (process.ExitCode != 0) { // grab stderr output for successful runs and log it anyway: MuPDF diagnostics, etc. come this way: var outs = process_output_reader.GetOutputsDumpStrings(); rv.errOutputDump = outs; Logging.Error("PDFDRAW did fail with exit code {0} for commandline:\n {3} {1}\n{2}", process.ExitCode, process_parameters, outs.stderr, pdfDrawExe); rv.error = new ApplicationException($"PDFRenderer::PDFDRAW did fail with exit code {process.ExitCode}.\n Commandline: {pdfDrawExe} {process_parameters}"); rv.exitCode = process.ExitCode; } else { // grab stderr output for successful runs and log it anyway: MuPDF diagnostics, etc. come this way: var outs = process_output_reader.GetOutputsDumpStrings(); rv.errOutputDump = outs; Logging.Error("PDFDRAW did SUCCEED with exit code {0} for commandline:\n {3} {1}\n{2}", process.ExitCode, process_parameters, outs.stderr, pdfDrawExe); rv.error = null; rv.exitCode = process.ExitCode; } return(rv); } } } } }