JetsonPDF.Reader
1.1.0
dotnet add package JetsonPDF.Reader --version 1.1.0
NuGet\Install-Package JetsonPDF.Reader -Version 1.1.0
<PackageReference Include="JetsonPDF.Reader" Version="1.1.0" />
<PackageVersion Include="JetsonPDF.Reader" Version="1.1.0" />
<PackageReference Include="JetsonPDF.Reader" />
paket add JetsonPDF.Reader --version 1.1.0
#r "nuget: JetsonPDF.Reader, 1.1.0"
#:package JetsonPDF.Reader@1.1.0
#addin nuget:?package=JetsonPDF.Reader&version=1.1.0
#tool nuget:?package=JetsonPDF.Reader&version=1.1.0
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
- Quick start
- Core concepts
- Loading a document
- File structure and encryption
- Pages and content streams
- Raw operator stream
- Colors and shading
- Fonts and text
- Filters
- Annotations
- Forms
- Document metadata
- Structure tree
- Measurement and viewports
- Conformance detection
- Limitations
- Targets
- License
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
xreftables 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
startxrefsections 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 4and/V 5security handlers). Supply a password toReader.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 thePdfFunctionevaluator 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,/Differencesmapped through Adobe Glyph List, or/ToUnicodewhen 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) —
/ToUnicodeis 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
/CIDSystemInfoplus/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 /URI → Uri; /A /GoTo /D or /Dest → InternalDestination. |
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.
JpxFilterreturns the original JPEG 2000 bitstream and extracts JP2ihdrmetadata (width / height / components / bit-depth). Decoding the codestream is left to downstream consumers — the WPF emitter writes it out as a.jp2file 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
nullfrom 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.Conformancereports the declaration only. Use the writer'sDocument.Validate()on a constructed document if you need rule-level checking. - Reader is read-only. Mutating a document means projecting the
ReadDocumentinto a writer-sideDocumentand emitting fresh bytes. For operator-level edits,RawOperators+Page.EmitOperatorround-trip every operator without re-interpretation. - Non-seekable streams are buffered. The PDF random-access model
requires it; the
Streamoverloads copy into aMemoryStreambefore parsing. Pass abyte[]directly to skip the copy.
Targets
net8.0netstandard2.0net462
License
MIT.
| Product | Versions 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. |
-
.NETFramework 4.6.2
- JetsonPDF.Common (>= 1.1.0)
- Microsoft.Bcl.HashCode (>= 6.0.0)
- System.Buffers (>= 4.5.1)
- System.Memory (>= 4.5.5)
- System.Text.Encoding.CodePages (>= 8.0.0)
- System.ValueTuple (>= 4.5.0)
-
.NETStandard 2.0
- JetsonPDF.Common (>= 1.1.0)
- Microsoft.Bcl.HashCode (>= 6.0.0)
- System.Buffers (>= 4.5.1)
- System.Memory (>= 4.5.5)
- System.Text.Encoding.CodePages (>= 8.0.0)
-
net8.0
- JetsonPDF.Common (>= 1.1.0)
- System.Text.Encoding.CodePages (>= 8.0.0)
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 |