JetsonPDF.Reader 1.1.0

dotnet add package JetsonPDF.Reader --version 1.1.0
                    
NuGet\Install-Package JetsonPDF.Reader -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="JetsonPDF.Reader" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="JetsonPDF.Reader" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="JetsonPDF.Reader" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add JetsonPDF.Reader --version 1.1.0
                    
#r "nuget: JetsonPDF.Reader, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package JetsonPDF.Reader@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=JetsonPDF.Reader&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=JetsonPDF.Reader&version=1.1.0
                    
Install as a Cake Tool

JetsonPDF.Reader

Headless, code-first reader for PDF files. Parses bytes that conform to ISO 32000-2 (PDF 2.0) into a navigable in-memory tree of immutable DTOs rooted on ReadDocument. No native dependencies, no JIT codegen, no STA threads; runs on net8.0, netstandard2.0, and net462.

using JetsonPDF.Reading;

ReadDocument doc = Reader.Load("input.pdf");

Console.WriteLine($"{doc.Pages.Count} pages, PDF {doc.PdfVersion}");
foreach (ReadPage page in doc.Pages)
{
    Console.WriteLine($"  page {page.MediaBox.Width}x{page.MediaBox.Height}");
    foreach (PageItem item in page.Items)
    {
        // PageTextItem / PagePathItem / PageImageItem / PageInlineImageItem
        // / PageShadingItem / PageFormXObjectItem
        Console.WriteLine($"    {item.GetType().Name}");
    }
    foreach (PageAnnotation anno in page.Annotations)
    {
        Console.WriteLine($"    annot {anno.GetType().Name} @ {anno.X},{anno.Y}");
    }
}

Contents


Overview

JetsonPDF.Reader is the inverse of JetsonPDF.Writer. Where the writer assembles an object graph and emits PDF bytes, the reader takes PDF bytes and hands back a ReadDocument that downstream tooling (the WPF XAML converter, the OpenSilver converter, the TIFF rasteriser, custom code) can walk without ever touching the underlying COS layer.

The shape mirrors the writer surface — every writer DTO has a matching ReadX (or PageX for content-stream items). Round-trip tooling can read a PDF, mutate the model, and re-emit through JetsonPDF.Writer without shifting domain types.

Decoding is lazy where it matters: content streams interpret on first access of ReadPage.Items; annotations parse on first access of ReadPage.Annotations; tiling patterns, viewports, associated files, and piece info all cache behind their property accessors. The cost of opening a 1000-page PDF is the file scan plus an xref/object-stream pass — page contents are touched only when you ask.

Quick start

Add a project reference to JetsonPDF.Reader and a using JetsonPDF.Reading;. Open from a path, a Stream, or a byte[], with an optional password for encrypted files:

using JetsonPDF.Reading;

ReadDocument fromPath   = Reader.Load("document.pdf");
ReadDocument fromStream = Reader.Load(File.OpenRead("document.pdf"));
ReadDocument fromBytes  = Reader.Load(File.ReadAllBytes("document.pdf"));

ReadDocument encrypted  = Reader.Load("locked.pdf", password: "hunter2");
if (!encrypted.EncryptionAuthenticated)
    throw new InvalidOperationException("Wrong password.");

Stream overloads buffer the input into memory; PDF requires random access, so a non-seekable Stream is copied into a MemoryStream first.

Core concepts

Type Role
Reader Static entry point. Reader.Load(path/stream/bytes [, password]).
ReadDocument The whole file: pages, outlines, named destinations, conformance, AcroForm, signatures, structure tree, XMP, etc.
ReadPage One page: MediaBox, lazy Items, Annotations, Viewports, AssociatedFiles, TilingPatterns, plus the raw operator stream.
PageItem Base class for things drawn by the content stream — PageTextItem, PagePathItem, PageImageItem, PageInlineImageItem, PageShadingItem, PageFormXObjectItem.
PageAnnotation Base class for /Annots overlays — links, markup, widgets, geometric, file attachments, etc.
WidgetAnnotation AcroForm widget with field name, type, value, flags, options, MK dict, actions, signature constraints.
Destination / NamedDestinations Explicit and named navigation targets recovered from the catalog.
ReadStructureTree Tagged-PDF tree with Roots, RoleMap, ClassMap.
Conformance Bit flags for PDF/A-1/2/3 and PDF/UA-1/2 declarations inferred from XMP.

Every public DTO is immutable post-construction; mutate by reading, projecting into a writer-side Document, and emitting fresh bytes.

Loading a document

// Path / stream / byte[], with optional password.
public static ReadDocument Load(string path);
public static ReadDocument Load(string path, string password);
public static ReadDocument Load(Stream input);
public static ReadDocument Load(Stream input, string password);
public static ReadDocument Load(byte[] data);
public static ReadDocument Load(byte[] data, string password);

The reader internally caches WinAnsi (Windows-1252) registration through Encoding.RegisterProvider on first call, so the same process can open files from any thread without re-registering.

File structure and encryption

PDF's binary layout is opaque to the consumer surface — ReadDocument already resolved every cross-reference. The file-structure features the reader handles transparently:

  • Classic xref tables and xref streams (§7.5.4 / §7.5.8) — both layouts resolve to the same indirect-object lookup.
  • Object streams (§7.5.7) — compressed indirect objects unpacked on demand.
  • Incremental updates — multiple startxref sections walked back to the oldest generation; the most recent write wins.
  • Linearized layouts ("Fast Web View", Annex F) — the linearization dict, two xrefs, and the hint stream are all recognised and traversed.
  • Encrypted documents — RC4-40, RC4-128, AES-128, and AES-256 (/V 4 and /V 5 security handlers). Supply a password to Reader.Load(...):
ReadDocument doc = Reader.Load("locked.pdf", password: "secret");
if (doc.IsEncrypted && !doc.EncryptionAuthenticated)
{
    // Wrong password; content streams will return their encrypted bytes.
}

ReadDocument.IsEncrypted reports whether the trailer declared /Encrypt; EncryptionAuthenticated reports whether the supplied password unlocked the file. Both must be true before content reads will return cleartext.

Pages and content streams

Pages are exposed through ReadDocument.Pages. Each ReadPage has a MediaBox and a lazy Items collection of PageItems extracted from the page's content stream.

foreach (ReadPage page in doc.Pages)
{
    foreach (PageItem item in page.Items)
    {
        switch (item)
        {
            case PageTextItem text:
                Console.WriteLine($"text {text.X},{text.Y}: \"{text.Text}\" " +
                                  $"({text.FontResource} {text.FontSize:0.#}pt, " +
                                  $"argb {text.FillArgb:X8})");
                break;
            case PagePathItem path:
                Console.WriteLine($"path \"{path.PathData}\" fill={path.DoFill} stroke={path.DoStroke}");
                break;
            case PageImageItem img:
                Console.WriteLine($"image {img.XObjectResource} {img.Width}x{img.Height}");
                break;
            case PageInlineImageItem inline:
                Console.WriteLine($"inline {inline.PixelWidth}x{inline.PixelHeight} " +
                                  $"{inline.ColorSpaceName} bpc={inline.BitsPerComponent}");
                break;
            case PageShadingItem sh:
                Console.WriteLine($"shading radial={sh.IsRadial} stops={sh.Stops.Count}");
                break;
        }
    }
}
Item Notes
PageTextItem Decoded Unicode Text, baseline origin (X, Y), FontResource, FontSize, FillArgb, optional per-glyph GlyphAdvances1000 + GlyphClusterLengths, optional RawBytes / TJElements for round-trip echo, OrientationDegrees for CTM-rotated runs.
PagePathItem WPF mini-language PathData in user space; DoFill/DoStroke/EvenOddFill; FillArgb/StrokeArgb; line width/cap/join/miter/dash; optional FillPatternName/StrokePatternName (tiling).
PageImageItem XObject reference + CTM-resolved rectangle. Resolve the actual bitmap by name through the page's resources.
PageInlineImageItem Pixel data and filter chain live on the item; no XObject indirection.
PageShadingItem Axial or radial gradient with packed stops, extend flags.
PageFormXObjectItem Only surfaced by ItemsPreservingForms; default Items flattens forms into their painted geometry.

Every PageItem also carries ClipPathData (current clip geometry in WPF mini-language, null if none), ClipEvenOdd, and BlendMode for the operator graphics-state in force at draw time.

// Keep Form XObject boundaries instead of flattening — useful for round-trip echo.
foreach (var item in page.ItemsPreservingForms)
{
    if (item is PageFormXObjectItem form)
        Console.WriteLine($"form {form.XObjectResource}");
}

Page-level extras:

page.Warnings           // soft errors from the content interpreter
page.Annotations        // /Annots overlays, see below
page.Viewports          // §12.10 viewports (rare)
page.AssociatedFiles    // §14.13 page-level /AF
page.PieceInfo          // §14.5 application piece info
page.TilingPatterns     // /Resources /Pattern entries with PatternType 1

Raw operator stream

For round-trip workflows that need every CTM, every q/Q, every marked-content scope, bypass Items and read RawOperators instead:

foreach (PdfOperator op in page.RawOperators)
{
    if (op.Op == "Tj" && op.TryGetStringBytes(0, out var bytes))
        Console.WriteLine($"Tj: {bytes.Length} encoded bytes");
}

PdfOperator.Op is the operator keyword, Operands is opaque (typed helpers TryGetNumber / TryGetName / TryGetStringBytes are provided). Hand the operator back to JetsonPDF.Page.EmitOperator(...) for verbatim re-emit — Tm/CTM matrices and graphics-state nesting are preserved without the interpreter folding them into PageItems.

Colors and shading

The content interpreter resolves colour into packed 0xAARRGGBB on every PagePathItem, PageTextItem, and PageShadingItem. The colour spaces the reader resolves before producing those packed values:

  • Device — DeviceGray, DeviceRGB, DeviceCMYK.
  • CIE-based — CalGray, CalRGB, Lab (honest CIE math).
  • ICCBased — single- and multi-component ICC profiles.
  • Indexed — palette-lookup through the base space.
  • Separation — single-colorant with the PdfFunction evaluator applied.
  • DeviceN / NChannel — multi-colorant with tint transform.
  • Pattern — both tiling (uncoloured + coloured) and shading variants.

PdfFunction Types 0 (sampled), 2 (exponential interpolation), 3 (stitching), and 4 (PostScript) all evaluate end-to-end. Functions on shading dicts, tint transforms, and stitched gradient stops all use the same evaluator.

foreach (var item in page.Items.OfType<PageShadingItem>())
{
    if (item.IsRadial)
        Console.WriteLine($"radial @ ({item.RadialCenterX}, {item.RadialCenterY}) r={item.RadialRadius}");
    else
        Console.WriteLine($"axial {item.LinearStartX},{item.LinearStartY} -> {item.LinearEndX},{item.LinearEndY}");

    foreach (var (offset, argb) in item.Stops)
        Console.WriteLine($"  stop {offset:0.000} {argb:X8}");
}

Shading types 1 (function-based), 2 (axial), and 3 (radial) collapse to PageShadingItem. Mesh shadings — types 4/5 (Gouraud triangles), 6 (Coons patches), 7 (tensor-product patches) — decode to triangle / patch lists behind the scenes; the interpreter folds them into stops where it can.

Fonts and text

PageTextItem.Text is already Unicode — the font's encoding has been applied. The strategies the reader picks from per font:

  • Type 1 simple fonts with /Encoding /WinAnsiEncoding, /MacRomanEncoding, /Differences mapped through Adobe Glyph List, or /ToUnicode when present.
  • TrueType / OpenType simple fonts with the same encoding routes; CID-keyed composite fonts use Identity-H or CMap.
  • Type 3 procedural fonts (writer-side feature) — /ToUnicode is preferred over AGL fallback so round-trip glyph names survive.
  • Standard 14 fonts use Adobe Core AFM metrics for ascent / descent / cap-height / glyph widths.
  • CID Identity-H composite fonts with stripped Unicode cmaps recover through a synthesised cmap built from the font's /CIDSystemInfo plus /ToUnicode.

PageTextItem.RawBytes and PageTextItem.TJElements carry the source operator's encoded bytes so callers can re-emit the run verbatim under the original font, bypassing decode-and-re-encode roundtrips that would lose glyph ordering for stripped CID fonts.

Vertical writing mode, /Span /ActualText marked content, and ToUnicode cluster decoding (multi-code-unit glyph clusters via GlyphClusterLengths) all flow through to the produced text.

Filters

Every standard PDF filter is supported on the stream-decoding path:

Filter Coverage
FlateDecode RFC 1950 zlib plus the four PNG predictors (None, Sub, Up, Average, Paeth) and PNG-style row prediction.
LZWDecode Variable-width LZW with the /EarlyChange switch.
ASCII85Decode Standard plus z shorthand.
ASCIIHexDecode Including the > end-of-data terminator.
RunLengthDecode §7.4.5 byte-pair RLE.
CCITTFaxDecode T.4 1D, T.4 2D (K > 0 mixed mode with per-row EOL+tag), T.6 (K < 0). Honours /EncodedByteAlign, /EndOfLine, /EndOfBlock, /BlackIs1.
JBIG2Decode MMR generic regions; arithmetic generic regions (templates 0/1/2/3 with AT-pixel overrides and TPGD prediction); symbol dictionaries; text regions (arithmetic and Huffman); refinement regions; pattern dictionaries; halftone regions (with rotation, skip bitmap). /JBIG2Globals is resolved through the file parser.
JPXDecode Passthrough plus JP2 ihdr width / height / component-count / bit-depth extraction — the encoded bitstream is handed off to consumers (the WPF emitter writes it as a .jp2).
DCTDecode JPEG passthrough (downstream consumes the JPEG bitstream directly).

Inline images (BI / ID / EI) feed the same filter pipeline through PageInlineImageItem.Filters + EncodedData.

Annotations

ReadPage.Annotations parses the /Annots array into typed subclasses of PageAnnotation. Each annotation reports its rect (X, Y, Width, Height), Contents text (with fallback to a linked /Popup), and any AssociatedFiles.

Annotation Coverage
LinkAnnotation /A /URIUri; /A /GoTo /D or /DestInternalDestination.
TextMarkupAnnotation Highlight / Underline / StrikeOut / Squiggly with QuadPoints quadrilaterals and /C colour.
FreeTextAnnotation /Contents text + /DA font + /Q quadding.
StampAnnotation /Name plus pre-rendered /AP /N appearance items.
LineAnnotation, SquareAnnotation, CircleAnnotation, PolygonAnnotation, PolyLineAnnotation, InkAnnotation Geometric markup. Common stroke / fill / border / dash live on the GeometricAnnotation base; each subclass adds its own geometry (L, Vertices, InkList, line endings).
CaretAnnotation /Sy symbol + colour.
FileAttachmentAnnotation /FS filespec resolved through /EF /UF (or /F fallback); embedded-file bytes decompressed; icon name surfaced.
PopupAnnotation /Open flag (most callers read Contents through the markup annotation that owns the popup).
SoundAnnotation Decoded sample bytes + /R//C//B//E metadata.
RedactAnnotation Overlay colour, overlay text, repeat flag, sub-region quads. JetsonPDF does not apply the destructive pass.
WidgetAnnotation AcroForm widget — see Forms.

Geometric markup annotations also pick up an inline /Measure dict (GeometricAnnotation.Measure) when present.

foreach (var anno in page.Annotations)
{
    switch (anno)
    {
        case LinkAnnotation link:
            Console.WriteLine($"link {link.Uri ?? link.InternalDestination}");
            break;
        case TextMarkupAnnotation tm:
            Console.WriteLine($"{tm.Kind} {tm.Quads.Count} quads argb={tm.Argb:X8}");
            break;
        case FileAttachmentAnnotation fa:
            File.WriteAllBytes(fa.FileName, fa.FileBytes);
            break;
        case WidgetAnnotation w:
            Console.WriteLine($"widget {w.FieldName} ({w.FieldType}) = {w.FieldValue}");
            break;
    }
}

Forms

AcroForm widgets land on WidgetAnnotation. Field-related state is inheritance-resolved up the /Parent chain per §12.7.4.2 before the widget is constructed, so a widget directly exposes its effective values:

var widgets = doc.Pages.SelectMany(p => p.Annotations).OfType<WidgetAnnotation>();

foreach (var w in widgets)
{
    Console.WriteLine($"{w.FieldName} ({w.FieldType}) = {w.FieldValue}");
    if (w.IsCheckBox)    Console.WriteLine($"  checked={w.IsChecked}");
    if (w.IsRadioButton) Console.WriteLine($"  appearance state={w.AppearanceState}");
    if (w.IsComboBox || w.IsListBox)
    {
        foreach (var (export, display) in w.Options)
            Console.WriteLine($"  option {export} -> {display}");
        Console.WriteLine($"  selected values: {string.Join(", ", w.SelectedValues)}");
    }
    if (w.IsMultiline) Console.WriteLine($"  multiline (max length {w.MaxLength})");

    if (w.Action is UriAction uri)        Console.WriteLine($"  action URI -> {uri.Uri}");
    if (w.Action is ResetFormAction rf)   Console.WriteLine($"  action ResetForm");
    if (w.Action is SubmitFormAction sf)  Console.WriteLine($"  action SubmitForm -> {sf.Url}");
}

Widget field types covered: Tx (text), Btn (push / check / radio), Ch (combo / list), Sig (signature), plus JetsonPDF's private /JetsonPDFBarcode round-trip for barcode fields (surfaced through WidgetAnnotation.Barcode as ReadBarcodeMetadata).

Decoded /Ff flag bits are reported through named properties — IsReadOnly, IsRequired, IsMultiline, IsPassword, IsRadioButton, IsPushButton, IsComboBox, IsEditableChoice, IsMultiSelect, IsFileSelect, DoNotSpellCheck, DoNotScroll, IsComb, IsRichText, RadiosInUnison, IsSorted, CommitOnSelChange.

Additional actions (/AA) are decoded into WidgetAnnotation.AdditionalActions, with named slots for Keystroke / Format / Validate / Calculate plus the mouse triggers Enter / Exit / Down / Up and the focus triggers Fo / Bl:

if (w.AdditionalActions is { } aa)
{
    if (aa.Keystroke is JavaScriptAction k) Console.WriteLine($"  /AA /K  -> {k.Script}");
    if (aa.Format    is JavaScriptAction f) Console.WriteLine($"  /AA /F  -> {f.Script}");
    if (aa.Calculate is JavaScriptAction c) Console.WriteLine($"  /AA /C  -> {c.Script}");
}

Document-level form pieces live on ReadDocument:

doc.CalculationOrder        // /AcroForm /CO — fully-qualified field names
doc.Signatures              // signed /FT /Sig fields (regular + DocTimeStamp)
doc.DocMdpPermission        // 1 / 2 / 3 from /Perms /DocMDP
doc.FieldMdpRestriction     // FieldMDP transform on certification signature
doc.SecurityStore           // /DSS + /VRI for PAdES-LT / LTA validation

Signature widgets also surface SignatureSeedValue (/SV) and SignatureLock (/Lock) constraints:

foreach (var w in widgets.Where(w => w.FieldType == "Sig"))
{
    if (w.SignatureSeedValue is { } sv)
        Console.WriteLine($"  filter={sv.Filter} subfilters={string.Join(",", sv.SubFilters)}");
    if (w.SignatureLock is { } lck)
        Console.WriteLine($"  lock action={lck.Action} fields={string.Join(",", lck.Fields)}");
}

Document metadata

ReadDocument lifts every catalog-level entry the writer can emit:

Property Source
PdfVersion Header %PDF-x.y + catalog /Version override.
Info Trailer /Info Title/Author/Subject/Producer/etc.
Outlines /Outlines tree as nested ReadOutlineItem.
PageLabels /PageLabels number tree. GetPageLabel(i) formats one.
NamedDestinations /Names /Dests name tree merged with legacy /Dests.
PageLayout, PageMode /PageLayout / /PageMode catalog enums.
ViewerPreferences /ViewerPreferences dict — display title, fit window, hide menubar, etc.
OpenAction /OpenAction as an explicit destination or a GoTo action.
XmpMetadata Raw XMP packet (UTF-8 string) from /Metadata stream.
OptionalContentGroups /OCProperties layers with intent + default visibility.
OutputIntents /OutputIntents array of ReadOutputIntent.
Language /Lang natural language.
AssociatedFiles /AF document-level associated files (Factur-X / ZUGFeRD land here).
PieceInfo /PieceInfo per-app metadata.
StructureTree /StructTreeRoot decoded — see below.
Conformance PDF/A and PDF/UA flags inferred from XMP.
foreach (var item in doc.Outlines)
    PrintOutline(item, depth: 0);

string label = doc.GetPageLabel(0);          // "i", "ii", "1", "A-1", ...
var dest     = doc.NamedDestinations["intro"];

if (doc.ViewerPreferences is { } vp)
    Console.WriteLine($"display doc title? {vp.DisplayDocTitle}");

Destination carries the resolved page index plus the explicit-destination type (Xyz, Fit, FitH, FitR, ...) so consumers don't have to re-walk /Kids.

Structure tree

Tagged-PDF documents decode into ReadDocument.StructureTree, which is either null (no tagged structure) or a ReadStructureTree with the nested element hierarchy plus the document-level RoleMap and ClassMap.

if (doc.StructureTree is { } tree)
{
    foreach (var root in tree.Roots)
        Visit(root, depth: 0);

    foreach (var (custom, standard) in tree.RoleMap)
        Console.WriteLine($"role {custom} -> {standard}");
}

void Visit(ReadStructureElement el, int depth)
{
    Console.WriteLine($"{new string(' ', depth * 2)}{el.StructureType} {el.Title}");
    foreach (var child in el.Children) Visit(child, depth + 1);
}

Each ReadStructureElement exposes StructureType (the /S name), Alt / ActualText / ExpansionAbbreviation / Language / Title / Id, marked-content IDs, OBJR references back to annotations (decoded to (pageIndex, annotationIndex) tuples), and a list of nested Children. Class names on /C resolve through ClassMap into shared ReadStructureAttributes bundles.

Measurement and viewports

Measurement-aware PDFs (engineering drawings, geospatial maps) decode their inline /Measure dicts and /VP viewport arrays:

foreach (var page in doc.Pages)
foreach (var vp in page.Viewports)
{
    Console.WriteLine($"viewport {vp.Name} bbox={vp.BBox}");
    if (vp.Measure is ReadRectilinearMeasure rl)
        Console.WriteLine($"  rectilinear scale {rl.ScaleRatio}");
    else if (vp.Measure is ReadGeospatialMeasure geo)
        Console.WriteLine($"  geospatial gcs={geo.Gcs?.EpsgCode}");
}

// Inline Measure on a geometric annotation.
foreach (var anno in page.Annotations.OfType<GeometricAnnotation>())
    if (anno.Measure is ReadRectilinearMeasure rl)
        Console.WriteLine($"  inline measure {rl.ScaleRatio}");

ReadNumberFormat chains (XAxis, YAxis, Distance, Area, Angle, Slope) are exposed raw so callers can format compound units (e.g. "5' 6 1/2"") in whatever convention they like. ReadCoordinateSystem carries either an EPSG code or a WKT string.

Conformance detection

ReadDocument.Conformance is a flags enum (Conformance.None when no declaration is present). The reader detects PDF/A-1b, PDF/A-2 a/u/b, PDF/A-3 a/u/b, PDF/UA-1, and PDF/UA-2 by parsing pdfaid:part / pdfaid:conformance / pdfuaid:part out of the XMP packet — the catalog flag is declarative, the reader does not validate content against the profile's rules.

if ((doc.Conformance & Conformance.PdfA2u) != 0)
    Console.WriteLine("Declares PDF/A-2u.");

foreach (var oi in doc.OutputIntents)
    Console.WriteLine($"output intent {oi.Subtype} {oi.OutputConditionIdentifier}");

For actual conformance checking (rule codes PDFA1x, PDFA2x, PDFA3x, PDFUA1, PDFUA2), use the writer-side validator: it walks a built JetsonPDF.Document and reports violations. The reader doesn't ship its own validator because the rules that matter are about the bytes the writer produced, not the bytes the reader parsed.

Limitations

  • JPX is passthrough. JpxFilter returns the original JPEG 2000 bitstream and extracts JP2 ihdr metadata (width / height / components / bit-depth). Decoding the codestream is left to downstream consumers — the WPF emitter writes it out as a .jp2 file and lets the OS image stack handle it.
  • JBIG2 still has open corners. Symbol dictionaries with variable-length inline Huffman code-lengths (§7.4.3.2) are not yet supported and surface a clear error path. Generic regions, text regions (arithmetic and Huffman), refinement, pattern dicts, and halftones all decode.
  • DCT (JPEG) is passthrough. PageImageItems reference the JPEG XObject by resource name; the reader hands consumers the encoded bytes and lets a JPEG decoder elsewhere produce pixels.
  • Multimedia and 3D annotations are not surfaced. Movie, Screen, 3D, RichMedia, and TrapNet subtypes return null from the annotation builder. They're rare in non-multimedia workflows and the underlying payloads (Flash, U3D, RichMedia) need runtimes the reader doesn't host.
  • Mesh-shading tessellation is incomplete. Types 4/5 decode to triangles; types 6/7 decode to patch lists with control points + corner colours. Bézier subdivision tessellation for types 6/7 is deferred — consumers that want pixel output need to tessellate themselves.
  • No conformance validation on the read side. ReadDocument.Conformance reports the declaration only. Use the writer's Document.Validate() on a constructed document if you need rule-level checking.
  • Reader is read-only. Mutating a document means projecting the ReadDocument into a writer-side Document and emitting fresh bytes. For operator-level edits, RawOperators + Page.EmitOperator round-trip every operator without re-interpretation.
  • Non-seekable streams are buffered. The PDF random-access model requires it; the Stream overloads copy into a MemoryStream before parsing. Pass a byte[] directly to skip the copy.

Targets

  • net8.0
  • netstandard2.0
  • net462

License

MIT.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (7)

Showing the top 5 NuGet packages that depend on JetsonPDF.Reader:

Package Downloads
JetsonPDF.Writer

Builds PDF documents from scratch, following ISO 32000-2.

JetsonPDF.Tiff

Managed (netstandard2.0) TIFF reader and writer used by JetsonPDF.OpenSilver to display .tif files in the browser without a JS dependency or server-side transcode, and by JetsonPDF.PdfToTiffConverter to emit TIFFs without GDI+. Decodes baseline TIFF strips (no compression, PackBits, CCITT G3 1D/2D, G4, LZW, Deflate) and the common photometric interpretations (WhiteIsZero, BlackIsZero, RGB, Palette, CMYK) to RGBA8888; encodes None / PackBits / Deflate. Includes a minimal PNG encoder so each frame can be served as a base64 data URI to OpenSilver's TiffViewer control.

JetsonPDF.Wpf

WPF integration for JetsonPDF. Bundles the XAML-to-PDF authoring pipeline (xmlns:jetsonpdf="http://schemas.jetsonpdf.com/authoring/2025" dialect, driven by real WPF Measure/Arrange) and the PDF-to-XAML viewer-emitter (turns a parsed ReadDocument into WPF XAML that XamlReader.Parse renders directly). Authoring types live under JetsonPDF.Wpf.Authoring; viewer/markup types live under JetsonPDF.Wpf. Windows-only (net8.0-windows + WPF).

JetsonPDF.OpenSilver

OpenSilver integration for JetsonPDF. Bundles both directions of the XAML pipeline. Authoring (XAML→PDF): the same xmlns:jetsonpdf="http://schemas.jetsonpdf.com/authoring/2025" dialect as the WPF integration, driven by the OpenSilver runtime so the same XAML compiles to a PDF from inside a WebAssembly app, an Edge WebView2 simulator, or a Playwright-driven Chromium CLI. Viewer (PDF→XAML): emits XAML that OpenSilver's XamlReader.Load renders in the browser — text via vector paths (no font cache), images via base64 data URIs, AcroForm widgets as live OpenSilver controls. Walker + image encoder + widget-action dispatch are OpenSilver-specific; emission code, snapshot model, and PDF emission are shared with the WPF flavour. Authoring types live under JetsonPDF.OpenSilver.Authoring; viewer/markup types under JetsonPDF.OpenSilver.

JetsonPDF.Forms

Open a PDF, discover and modify its AcroForm fields (text, image stamps, choices, checkboxes), and save back via single-layer incremental update.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 478 6/6/2026
1.0.0 417 5/23/2026
0.2.0-preview 392 5/23/2026
0.1.0-preview 347 5/17/2026