A cross-platform .NET Global Tool for PDF editing — starts with separate (split a PDF into single-page files) and is designed to grow into merge, encrypt, and more without touching existing code.
- Target framework: .NET 10
- CLI framework: System.CommandLine 2.0 (GA / stable)
- PDF engine: PdfSharp 6.2 (MIT licensed)
- Architecture: Command pattern via
ICliCommand— one class per subcommand.
# 1. Build & pack
dotnet pack pdf-tools.slnx -c Release
# 2. Install globally (from the solution root)
dotnet tool install --global PdfTool \
--add-source ./nupkg --version 0.2.0dotnet tool update --global PdfTool --add-source ./nupkg
dotnet tool uninstall --global PdfTool
~/.dotnet/toolsmust be on yourPATH(the dotnet installer usually adds it).
pdf-tool separate <file> [--output <dir>] [--pages <range>]
| Flag | Alias | Description |
|---|---|---|
<file> |
— | Path to the source PDF. Required. |
--output |
-o |
Output directory. Defaults to the source file's directory. |
--pages |
-p |
Page selection (e.g. 1-3,5,8-10). Omit to extract every page. |
Output file naming: {originalName}_page_{n}.pdf
# Split every page into its own file (next to the source)
pdf-tool separate report.pdf
# Into a specific directory
pdf-tool separate report.pdf --output ./split
# Extract pages 1, 2, 3 and 5 only
pdf-tool separate report.pdf -o ./split -p "1-3,5"
# Extract a single page
pdf-tool separate report.pdf -p "7"| Code | Meaning |
|---|---|
0 |
Success |
2 |
File not found / not a .pdf |
3 |
Output directory cannot be created |
4 |
Invalid PDF format / unreadable |
5 |
Source PDF has zero pages |
6 |
--pages range is invalid |
7 |
Write failure |
- .NET 10 SDK or newer (
dotnet --version≥ 10.0)
# Restore + build
dotnet build pdf-tools.slnx -c Release
# Run without installing
dotnet run --project PdfTool -- separate sample.pdf -o outpdf-tools/
├── pdf-tools.slnx # Solution (.NET 9+ XML format)
├── .editorconfig # Shared coding conventions
├── .gitignore # dotnet build artifacts
├── .gitattributes # Cross-platform line endings
├── README.md
├── CLAUDE.md # Claude Code collaboration guide
├── nupkg/ # Built .nupkg output
└── PdfTool/
├── PdfTool.csproj # Global Tool metadata
├── Program.cs # RootCommand registration only
└── Commands/
├── ICliCommand.cs # Command-pattern contract
├── PageRangeParser.cs # "1-3,5" → [1,2,3,5]
└── SeparateCommand.cs # `separate` implementation
-
Create
PdfTool/Commands/{Name}Command.csimplementingICliCommand. -
Register it in
PdfTool/Program.cs:ICliCommand[] commands = { new SeparateCommand(), new MergeCommand(), // ← add here };
That's it — Program.cs doesn't need to know about arguments or options.
pdf-tool targets .NET 10 and works on Windows, macOS, and Linux (x64 and arm64).
Design choices that keep it portable:
- All paths use
System.IO.Path.CombineandFileInfo/DirectoryInfo— no hard-coded separators. - No P/Invoke, no OS-specific APIs.
- Line endings are normalized through
.gitattributes/.editorconfigso the same source compiles identically on Windows (CRLF) and Unix (LF).
- PdfSharp font resolver on non-Windows: PdfSharp's
XFontneeds a font resolver on Linux/macOS (it looks for Windows system fonts by default). This tool'sseparatecommand does not render text, so it is unaffected. Future commands that draw text (e.g. watermarking) will need to registerGlobalFontSettings.FontResolverexplicitly.
-
separate— split PDF into single-page files -
merge— combine multiple PDFs into one -
encrypt— password-protect a PDF (owner/user passwords) -
rotate— rotate selected pages -
extract-text— dump text content
MIT. PdfSharp is MIT licensed. System.CommandLine is MIT licensed.