A high-performance, GPU-accelerated PDF rendering engine written in C++ with a .NET wrapper and WPF viewer control.
Developed by The Big Studio
Website & Screenshots | Download Setup (Windows x64)
Alpha Release - This project is in active development. APIs may change between versions. Not recommended for production use yet. Feedback and bug reports are welcome!
ManasPDF is a from-scratch PDF rendering engine built on Direct2D for GPU-accelerated rendering with an automatic CPU software renderer fallback. The core engine is a native C++ DLL (ManasPDFCore.dll) that handles all PDF parsing, content stream interpretation, font rasterization, and pixel output. A .NET wrapper and WPF control are provided for easy integration into C# applications.
- Direct2D GPU renderer - Hardware-accelerated path rasterization, gradient shading, image compositing
- CPU software renderer - Pure C++ scanline rasterizer with configurable SSAA (1x/2x/4x)
- Automatic fallback - GPU failure (device lost, remote desktop) falls back to CPU seamlessly
- Render quality modes - Fast (no SSAA), Standard (SSAA=1), Quality (SSAA=2)
- BGRA32 output - Raw pixel buffer, ready for display or further processing
- Cross-reference table and stream parsing
- Object streams, indirect references, stream filters
- Page tree traversal with inherited attributes
- 90+ PDF operators implemented (paths, text, color, shading, clipping, XObjects)
- FreeType font rasterization (TrueType, CFF/PostScript, Type1)
- Type0/CID fonts with CID-to-GID mapping
- Custom encodings and ToUnicode CMap support
- Glyph-level text extraction with pixel coordinates
| Codec | Filter | Library |
|---|---|---|
| JPEG | DCTDecode | libjpeg-turbo |
| JPEG2000 | JPXDecode | OpenJPEG (x64) |
| Flate/Deflate | FlateDecode | zlib |
| LZW | LZWDecode | Built-in |
| CCITT Fax | CCITTFaxDecode | Built-in (Group 3/4) |
| ASCII85 | ASCII85Decode | Built-in |
| RunLength | RunLengthDecode | Built-in |
| PNG Predictor | - | Built-in (Up/Sub/Avg/Paeth) |
Filter chains (multiple filters in sequence) are fully supported.
DeviceRGB, DeviceCMYK, DeviceGray, CalRGB, CalGray, ICCBased, Indexed, Separation, DeviceN, Lab
- Paths: moveto, lineto, cubic bezier, rectangle, close
- Fill rules: even-odd, non-zero winding
- Stroke: line width, cap styles, join styles, miter limit, dash patterns
- Gradients: axial (linear) and radial with LUT-based evaluation + dithering
- Tiling patterns with arbitrary transforms
- Nested clipping paths (Form XObject clip layer stacking)
- Password encryption (/Standard handler)
- RC4 40-128 bit (Revision 2-3)
- AES-128 / AES-256 (Revision 5)
- Certificate encryption (/Adobe.PubSec)
- PKCS#7 EnvelopedData parsing
- RSA key transport with recipient enumeration
- Full ASN.1 DER parser for certificate matching
- Page render cache - Per-document, per-zoom LRU cache (500MB limit)
- Glyph cache - Rasterized glyphs by font hash + glyph ID + pixel size (128MB / 20K glyphs)
- Font cache - FT_Face objects cached by font program hash (100 fonts max)
- Text batching - BT/ET glyph atlas composition reduces draw calls (GPU)
- Active document filter - Multi-tab optimization, skip rendering for inactive documents
- Zero-copy interop - Cache results copied directly to output buffer
+---------------------------+
| Your Application |
+---------------------------+
|
+----------+----------+
| |
+-------v-------+ +--------v--------+
| ManasPDF | | ManasPDF.Wpf |
| (.NET Wrapper)| | (WPF Control) |
| NuGet pkg | | NuGet pkg |
+-------+-------+ +-----------------+
|
P/Invoke (C interop)
|
+---------v-----------+
| ManasPDFCore.dll |
| (C++ Engine) |
+-----+-------+------+
| |
+--------v--+ +--v---------+
| Direct2D | | Software |
| GPU | | CPU |
| Renderer | | Renderer |
+-----------+ +------------+
| |
+----------+-------+-----------+
| FreeType | zlib | |
| libjpeg | OpenJPEG | |
+------------------------------+
Pdf_RenderPageToRgba(doc, pageIndex, zoom)
|
+-> Check PageRenderCache (LRU, zero-copy)
| |-> Cache hit: copy to output buffer, return
| |-> Cache miss: continue
|
+-> Acquire render mutex
+-> PdfPainterGPU::initialize(width, height)
+-> PdfDocument::renderPageToPainter()
| |-> PdfContentParser::parse(contentStream)
| |-> Graphics state operators (q, Q, cm, w, J, ...)
| |-> Path operators (m, l, c, re, h, ...)
| |-> Paint operators (f, S, B, n, ...)
| |-> Text operators (BT, ET, Tf, Tm, Tj, TJ, ...)
| |-> Color operators (CS, SC, RG, K, G, ...)
| |-> XObject operators (Do -> Form/Image)
| |-> Clipping operators (W, W*)
|
+-> PdfPainterGPU::getBuffer() -> BGRA32 pixels
+-> Store in PageRenderCache
+-> Return to caller
ID2D1RenderTarget+IWICBitmapper render instance- Static D2D/WIC/DirectWrite factories shared across all instances
- Glyph atlas batching: glyphs collected during BT/ET blocks, rendered as a single atlas texture (threshold: 1000 glyphs, 4MB atlas)
- Brush caching: D2D solid color brushes cached by BGRA value
- Clip layer stack: nested Form XObjects use D2D clip layers
- Max bitmap: 16384x16384 (1GB RGBA)
- Scanline polygon rasterization
- Configurable supersampling (SSAA 1x/2x/4x)
- Direct pixel blending for grayscale glyphs
- Gradient evaluation with 4096-sample LUT + dithering
- No GPU/DirectX dependency
The native library exports a flat C API for cross-language interop:
// Document
PDF_API void* Pdf_OpenDocument(const wchar_t* path);
PDF_API void Pdf_CloseDocument(void* doc);
PDF_API int Pdf_GetPageCount(void* doc);
PDF_API int Pdf_GetPageSize(void* doc, int pageIndex, double* w, double* h);
PDF_API int Pdf_GetPageRotate(void* doc, int pageIndex);
// Rendering (BGRA32 output)
PDF_API int Pdf_RenderPageToRgba(void* doc, int pageIndex, double zoom,
void* buffer, int bufferSize,
int* outWidth, int* outHeight);
PDF_API int Pdf_RenderPageToRgba_CPU(void* doc, ...); // CPU-only
PDF_API int Pdf_RenderPageToRgba_Fast(void* doc, ...); // No SSAA
PDF_API int Pdf_RenderPageToRgba_Quality(void* doc, ...); // SSAA=2
// Text extraction
PDF_API int Pdf_ExtractPageText(void* doc, int pageIndex);
PDF_API int Pdf_GetTextGlyphCount(void* doc, int pageIndex);
PDF_API int Pdf_GetExtractedGlyphs(void* doc, int pageIndex,
PdfTextGlyphExport* out, int maxCount);
PDF_API int Pdf_GetExtractedTextUtf8(void* doc, int pageIndex,
char* outBuffer, int maxLen);
// Links
PDF_API int Pdf_GetPageLinkCount(void* doc, int pageIndex);
PDF_API int Pdf_GetPageLinks(void* doc, int pageIndex, ...);
// Encryption
PDF_API int Pdf_GetEncryptionStatus(void* doc); // 0=none, 1=ready, -1=locked
PDF_API int Pdf_GetEncryptionType(void* doc); // 0=none, 1=password, 2=cert
PDF_API int Pdf_TryPassword(void* doc, const char* password);
PDF_API int Pdf_SupplyCertSeed(void* doc, const uint8_t* seed, int seedLen);
PDF_API int Pdf_GetCertRecipientCount(void* doc);
PDF_API int Pdf_GetCertRecipientEncryptedKey(void* doc, int idx, ...);
// Cache management
PDF_API void Pdf_ClearDocumentCache(void* doc);
PDF_API void Pdf_ClearAllCache();
PDF_API void Pdf_SetActiveDocument(void* doc);struct PdfTextGlyphExport {
uint32_t unicode; // Unicode code point
float x, y; // Position in bitmap pixels (at zoom=1, 96 DPI)
float width; // Glyph advance width
float height; // Glyph height
float fontSize; // Effective font size in pixels
};NuGet packages provide a managed wrapper and a ready-to-use WPF viewer control.
| Package | Description | Target |
|---|---|---|
| ManasPDF | .NET wrapper over the native engine | net8.0 |
| ManasPDF.Wpf | Drop-in WPF PdfViewer control with toolbar | net8.0-windows |
dotnet add package ManasPDF.Wpf --prerelease
ManasPDF.Wpfautomatically includes theManasPDFcore package. Use--prereleasefor alpha versions.
using ManasPDF;
using var doc = PdfDocument.Open("sample.pdf");
var page = doc.GetPage(0);
// GPU-accelerated render -> BGRA32 pixels
byte[]? pixels = page.Render(1.5, out int width, out int height);
// Text extraction
string text = page.ExtractText();
PdfTextGlyph[] glyphs = page.ExtractGlyphs();
// Hyperlinks
PdfLink[] links = page.GetLinks();<Window xmlns:manaspdf="clr-namespace:ManasPDF.Wpf;assembly=ManasPDF.Wpf">
<manaspdf:PdfViewer x:Name="Viewer" Source="C:\docs\sample.pdf" />
</Window>Built-in features: zoom, page navigation, text search & highlight, text selection & copy, rotation, print preview with printer selection, save as, hyperlink click, keyboard shortcuts (Ctrl+F/P/S/C).
Full .NET API documentation: DOTNET-API.md
- Visual Studio 2022 with C++ Desktop workload
- .NET 8.0 SDK
- CMake 3.20+
cd src/PDFCore
cmake -B build -A x64
cmake --build build --config Release
# Output: ManasPDFCore.dlldotnet build src/ManasPDF -c Release
dotnet build src/ManasPDF.Wpf -c ReleasePlace native DLLs into the runtimes folder, then pack:
src/ManasPDF/runtimes/win-x64/native/
ManasPDFCore.dll
freetype.dll
jpeg62.dll
openjp2.dll
dotnet pack src/ManasPDF -c Release
dotnet pack src/ManasPDF.Wpf -c ReleaseManasPDF/
src/
PDFCore/ # C++ native engine (ManasPDFCore.dll)
PdfEngine.h # DLL export declarations
PdfDocument.cpp/h # PDF parsing, encryption, font loading
PdfContentParser.cpp/h # Content stream operator interpreter
PdfPainterGPU.cpp/h # Direct2D GPU renderer
PdfPainter.cpp/h # CPU software renderer
PdfTextExtractor.cpp/h # Text extraction
GlyphCache.cpp/h # Glyph bitmap cache
PdfGradient.cpp/h # Gradient rendering
PdfFilters.cpp/h # Stream decompression (7 codecs)
IPdfPainter.h # Abstract renderer interface
PdfGraphicsState.h # Graphics state model
PdfPath.h # Path representation
PdfObject.h # PDF object model
PdfParser.cpp/h # Object parser
PdfLexer.cpp/h # Tokenizer
CMakeLists.txt # Build configuration
ManasPDF/ # .NET wrapper (NuGet: ManasPDF)
PdfDocument.cs # High-level API
PdfPage.cs # Page operations
PdfTextGlyph.cs # Glyph struct (24-byte interop)
PdfLinkExport.cs # Link classes
PdfException.cs # Exception type
Internal/NativeApi.cs # P/Invoke bindings
ManasPDF.Wpf/ # WPF control (NuGet: ManasPDF.Wpf)
PdfViewer.xaml # UserControl layout
PdfViewer.xaml.cs # Control logic
DOTNET-API.md # .NET API reference
LICENSE # Apache 2.0
THIRD-PARTY-NOTICES.txt # Third-party licenses
README.md
| Library | File | Purpose | License |
|---|---|---|---|
| FreeType | freetype.dll |
Font rasterization | FTL/GPLv2 |
| libjpeg-turbo | jpeg62.dll |
JPEG decoding | IJG/BSD |
| OpenJPEG | openjp2.dll |
JPEG2000 decoding (x64) | BSD-2 |
| zlib | statically linked | Deflate compression | zlib |
Planned: HarfBuzz integration for complex text shaping (Arabic, Devanagari, etc.) is planned for a future release.
See THIRD-PARTY-NOTICES.txt for full license texts.
Bug Fixes
- Fixed Turkish character rendering (ğ, ş, ı, İ, Ş, Ğ) in subset TrueType fonts with /Differences encoding
Bug Fixes
- Fixed garbled text rendering for subset TrueType fonts with custom ToUnicode CMap
- WinAnsi glyph names no longer override ToUnicode mappings for fonts without explicit
/Encoding - Added symbolic cmap (0xF000+code) fallback per PDF spec for TrueType fonts with (3,0) cmap
- Subset symbolic fonts (no glyph names, Mac-only cmap): direct code→GID mapping now takes priority over Unicode→GID lookup to prevent incorrect glyph substitution
- WinAnsi glyph names no longer override ToUnicode mappings for fonts without explicit
- Fixed rendering of complex gradient overlays using nested tiling patterns and rotated images with SMask
- PdfStream objects in pattern resolution now handled correctly (fallback from PdfDictionary cast)
- Tiling pattern tile size limit increased from 2048 to 4096 with dynamic scale calculation for large BBox patterns
- Fixed double premultiplied alpha in tiling pattern bitmap brush creation
- Proper brush transform composition: bitmap Y-flip → pattern matrix → default CTM → device scale
- Unresolvable pattern fills now skip gracefully instead of falling back to solid black
- Rotated images with rect clip now use bitmap brush transform instead of AABB stretch (rotation was lost)
- Fixed pre-scaling dimension calculation for rotated images (per-axis extent instead of AABB)
- Fixed UI overflow on multi-monitor setups with different DPI scales (e.g., 1080p main + 1440p secondary)
- Added
WM_DPICHANGEDhandler: updates DPI scale and applies suggested window rect when dragging between monitors - Clears render cache and re-renders visible pages at new DPI automatically
- Added
Memory Optimization
- Eliminated redundant buffer copies during page rendering (peak memory reduced by ~50%)
- Added
getBufferDirect()to GPU painter: writes directly from WIC bitmap to output buffer - Added
getDownsampledBufferDirect()to CPU painter: downsamples directly into output buffer - Added
storeFromRaw()to page render cache: stores from raw pointer without extra vector copy
- Added
- Proactive cache trimming: page cache is cleared before large renders when total exceeds 400MB
- Large single entries (>125MB) are no longer cached to prevent cache thrashing
Crash Prevention
- Added
std::bad_allocprotection during GPU and CPU rendering: clears page + glyph caches on OOM - Added
std::bad_allocprotection in cache store: gracefully skips caching if allocation fails - Added
OutOfMemoryExceptionprotection forWriteableBitmapcreation on C# side
Zoom Improvements
- Instant zoom scaling for all pages — all pages resize immediately on zoom, not just visible ones
- Mouse-centered zoom — Ctrl+Scroll zoom anchors to mouse position instead of jumping to another page
- Viewport-anchored toolbar zoom — +/- buttons keep the current page in view
PDF Rendering Fixes
- CropBox origin offset — correct initial CTM for PDFs with non-zero CropBox/MediaBox origin
- Text quality improvement — snap glyph cache scale to 1.0 when within 5% for sharper text rendering
- Form XObject /BBox clipping — apply BBox boundary as clip rect when rendering Form XObjects
- D2D clip layer cleanup — pop unpaired PushLayer calls at end of content stream parsing, preventing GPU EndDraw failure and CPU fallback, fixes page clipping for PDFs using shared Form XObjects
Apache License 2.0 - see LICENSE for details.
ManasPDF is a product of The Big Studio | Report an Issue
