namespace Pdftract; /// /// Represents a PDF source (file path, URL, or raw bytes). /// public abstract class Source { /// /// Returns command-line arguments for the source. /// internal abstract List ToArgs(); /// /// Performs cleanup (e.g., deletes temporary files). /// internal virtual void Dispose() { } /// /// Creates a Source from a local file path. /// public static Source FromPath(string path) => new PathSource(path); /// /// Creates a Source from a URL string. /// public static Source FromUrl(string url) => new UrlSource(url); /// /// Creates a Source from a URI. /// public static Source FromUri(Uri uri) => new UrlSource(uri.ToString()); /// /// Creates a Source from a byte array. /// public static Source FromBytes(byte[] data) => new BytesSource(data); /// /// Creates a Source from a file by reading it into memory. /// public static Source FromFileBytes(string path) { var data = File.ReadAllBytes(path); return new BytesSource(data); } } /// /// A local filesystem path source. /// public sealed class PathSource : Source { private readonly string _path; public PathSource(string path) { _path = Path.GetFullPath(path); } internal override List ToArgs() { return new() { _path }; } } /// /// A remote URL source. /// public sealed class UrlSource : Source { private readonly string _url; public UrlSource(string url) { if (!url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && !url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("URL must start with http:// or https://", nameof(url)); } _url = url; } internal override List ToArgs() { return new() { "--url", _url }; } } /// /// An in-memory byte array source. /// Creates a temporary file that is cleaned up after use. /// public sealed class BytesSource : Source { private readonly byte[] _data; private string? _tmpPath; public BytesSource(byte[] data) { _data = data ?? throw new ArgumentNullException(nameof(data)); } internal override List ToArgs() { if (_tmpPath != null) { return new() { _tmpPath }; } var tmpFile = Path.GetTempFileName(); File.WriteAllBytes(tmpFile, _data); _tmpPath = tmpFile; return new() { _tmpPath }; } internal override void Dispose() { try { if (_tmpPath != null && File.Exists(_tmpPath)) { File.Delete(_tmpPath); } } catch { // Ignore cleanup errors } _tmpPath = null; } }