PdfPig
| Language | C# (.NET Standard 2.0, .NET 6+, .NET Framework 4.6.2+) |
|---|---|
| License | Apache-2.0 |
| Repository | github.com/UglyToad/PdfPig |
| Documentation | README and wiki |
| Depends on | nothing (fully managed) |
| Read / write | yes / basic: text, letters, words, images, annotations, metadata; create pages with text and graphics, merge |
PdfPig is a port of the parsing parts of Apache PDFBox to C#, with no native dependencies. It reads every letter with its font, size and bounding box, and includes document layout analysis tools: word extractors, page segmenters (Docstrum, recursive XY cut) and reading-order detection, useful for extracting text from multi-column documents or feeding PDFs to search and machine-learning pipelines. It opens encrypted files with a password.
PdfDocumentBuilder creates simple documents (standard 14 or TrueType fonts, lines, rectangles, images) and copies pages from existing files, and PdfMerger merges files. PdfPig doesn’t render pages, fill forms or encrypt. The version is below 1.0, so minor releases can change the API.
Installation#
dotnet add package PdfPig
Basic usage: extract text from a PDF#
using UglyToad.PdfPig;
using UglyToad.PdfPig.Content;
using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor;
using PdfDocument document = PdfDocument.Open("in.pdf");
Console.WriteLine($"{document.NumberOfPages} pages, PDF {document.Version}");
foreach (Page page in document.GetPages())
{
// text in content order, with line breaks and spaces inferred from positions
string text = ContentOrderTextExtractor.GetText(page);
Console.WriteLine($"--- page {page.Number} ({page.Width} x {page.Height} pt) ---");
Console.WriteLine(text);
}
page.Text returns the raw text in the order it appears in the content stream, without inferred spaces or line breaks: usually not what you want.
How to get words and their positions#
using UglyToad.PdfPig.DocumentLayoutAnalysis.WordExtractor;
Page page = document.GetPage(1); // pages are numbered from 1
foreach (var word in page.GetWords(NearestNeighbourWordExtractor.Instance))
{
var box = word.BoundingBox;
Console.WriteLine($"{word.Text} at ({box.Left:F1}, {box.Bottom:F1}) font {word.FontName}");
}
foreach (var letter in page.Letters.Take(5))
Console.WriteLine($"{letter.Value} size {letter.PointSize}");
How to read PDF metadata#
using PdfDocument document = PdfDocument.Open("in.pdf");
var info = document.Information;
Console.WriteLine($"Title: {info.Title}");
Console.WriteLine($"Author: {info.Author}");
Console.WriteLine($"Producer: {info.Producer}");
Console.WriteLine($"Created: {info.CreationDate}");
How to open a password-protected PDF#
using UglyToad.PdfPig;
using var document = PdfDocument.Open("secret.pdf", new ParsingOptions { Password = "password" });
How to merge PDF files#
using UglyToad.PdfPig.Writer;
byte[] merged = PdfMerger.Merge("a.pdf", "b.pdf", "c.pdf");
File.WriteAllBytes("merged.pdf", merged);
How to create a PDF with text#
using UglyToad.PdfPig.Content;
using UglyToad.PdfPig.Core;
using UglyToad.PdfPig.Fonts.Standard14Fonts;
using UglyToad.PdfPig.Writer;
var builder = new PdfDocumentBuilder();
PdfPageBuilder page = builder.AddPage(PageSize.A4);
PdfDocumentBuilder.AddedFont font = builder.AddStandard14Font(Standard14Font.Helvetica);
page.AddText("Hello, PDF!", 24, new PdfPoint(72, 750), font);
page.DrawLine(new PdfPoint(72, 740), new PdfPoint(300, 740));
File.WriteAllBytes("hello.pdf", builder.Build());
Standard 14 fonts support only Latin-1 text; register a TrueType font with builder.AddTrueTypeFont(File.ReadAllBytes("font.ttf")) for other characters.
How to extract images#
foreach (var image in document.GetPage(1).GetImages())
{
if (image.TryGetPng(out byte[] png))
File.WriteAllBytes($"image-{Guid.NewGuid()}.png", png);
else
File.WriteAllBytes($"image-{Guid.NewGuid()}.bin", image.RawBytes.ToArray()); // e.g. JPEG data
}
Tips#
PdfDocument.Openaccepts abyte[]orStreamas well as a path.ParsingOptions { UseLenientParsing = true }(the default) tolerates many broken files; setClipPaths = trueto get clipping paths for vector analysis.- For multi-column layouts, segment the page with
DocstrumBoundingBoxes.Instance.GetBlocks(words)and order the blocks withUnsupervisedReadingOrderDetector.Instance.Get(blocks).