JetsonPDF.OpenSilver
1.0.0
dotnet add package JetsonPDF.OpenSilver --version 1.0.0
NuGet\Install-Package JetsonPDF.OpenSilver -Version 1.0.0
<PackageReference Include="JetsonPDF.OpenSilver" Version="1.0.0" />
<PackageVersion Include="JetsonPDF.OpenSilver" Version="1.0.0" />
<PackageReference Include="JetsonPDF.OpenSilver" />
paket add JetsonPDF.OpenSilver --version 1.0.0
#r "nuget: JetsonPDF.OpenSilver, 1.0.0"
#:package JetsonPDF.OpenSilver@1.0.0
#addin nuget:?package=JetsonPDF.OpenSilver&version=1.0.0
#tool nuget:?package=JetsonPDF.OpenSilver&version=1.0.0
JetsonPDF.OpenSilver
OpenSilver integration for JetsonPDF. Authors the same
xmlns:jetsonpdf="http://schemas.jetsonpdf.com/authoring/2025" dialect as the WPF integration, but the layout pass runs on the OpenSilver runtime so the same XAML can compile to a PDF from inside a WebAssembly app, an Edge WebView2 simulator, or a Playwright-driven Chromium CLI.
using JetsonPDF.OpenSilver.Authoring;
string xaml = """
<jetsonpdf:Document xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:jetsonpdf="http://schemas.jetsonpdf.com/authoring/2025"
Title="OpenSilver demo">
<jetsonpdf:Document.Pages>
<jetsonpdf:Page Width="612" Height="792">
<Canvas>
<Rectangle Canvas.Left="72" Canvas.Top="72" Width="144" Height="72"
Fill="#1E88E5"/>
<TextBlock Canvas.Left="72" Canvas.Top="160" Text="Hello from OpenSilver"
FontFamily="Helvetica" FontSize="18"/>
</Canvas>
</jetsonpdf:Page>
</jetsonpdf:Document.Pages>
</jetsonpdf:Document>
""";
byte[] pdf = await XamlToPdfConverter.ConvertAsync(xaml);
// or: await XamlToPdfConverter.ConvertAsync(xaml, outputStream, options);
The result is identical (snapshot-level) to what JetsonPDF.Wpf produces from the same XAML — both walkers feed the same runtime-neutral DocumentSnapshot into JetsonPDF.XamlToPdfConverter.Core.ConverterCore.
Public API
| Member | Purpose |
|---|---|
JetsonPDF.OpenSilver.Authoring.XamlToPdfConverter.ConvertAsync(string xaml, XamlToPdfCoreOptions? options = null) → Task<byte[]> |
Parse, layout, walk, emit PDF bytes. |
JetsonPDF.OpenSilver.Authoring.XamlToPdfConverter.ConvertAsync(string xaml, Stream output, XamlToPdfCoreOptions? options = null) → Task |
Same, but writes to a stream. |
JetsonPDF.OpenSilver.Authoring.OpenSilverTreeWalker |
Walker (implements IXamlTreeWalker). Set HostContainer to a hosted Panel for accurate layout in a Wasm host (see Hosting). |
JetsonPDF.OpenSilver.Base64ImageExtension |
{jetsonpdf:Base64Image Data='…'} markup extension that wraps the payload in a data: URI so OpenSilver's browser-backed BitmapImage can decode it natively. |
JetsonPDF.OpenSilver.IJpegPixelDecoder / IJpxPixelDecoder |
Optional hooks for decoding JPEG / JPEG 2000 bytes to RGB pixels. Register one when you need JPEG+SMask alpha compositing (see JPEG + SMask alpha). |
JetsonPDF.OpenSilver.OpenSilverImageDecoders |
Static slots (.Jpeg, .Jpx) where consumers plug in the decoders above. |
JetsonPDF.OpenSilver.PdfToTiffBrowserConverter.ConvertAsync(byte[] pdfBytes, Panel host, TiffWriteOptions? options = null, IProgress<TiffConversionProgress>? progress = null) → Task<byte[]> |
Rasterise every page of a PDF to a multipage TIFF, entirely in the browser (see Browser-side PDF → TIFF). |
The XAML namespace is registered as xmlns:jetsonpdf="http://schemas.jetsonpdf.com/authoring/2025" and resolves both JetsonPDF.OpenSilver.Authoring (containers/document/page types from the shared authoring projitems) and JetsonPDF.OpenSilver (the OpenSilver-side Base64ImageExtension).
Supported XAML surface
The OpenSilver walker handles the full authoring dialect that the WPF integration does (with two caveats noted below). All emission goes through runtime-neutral snapshots, so anything the core emitter knows how to draw is reachable from OpenSilver:
- Document structure —
<jetsonpdf:Document>root with multiple<jetsonpdf:Page>children, per-page width/height/landscape, document metadata (Title/Author), named destinations, page labels. - Shapes —
Rectangle(with corner radii),Ellipse,Line,Border,Path(LineGeometry / RectangleGeometry / EllipseGeometry / PathGeometry with LineSegment, BezierSegment, QuadraticBezier, PolyLineSegment, PolyBezierSegment, PolyQuadraticBezierSegment, ArcSegment),Image. - Text —
TextBlockwith FontFamily/FontSize/FontStyle/FontWeight/Foreground, TextWrapping, TextAlignment, mixed-style<Run>inlines. - Annotations — Link, TextMarkup (Highlight/Underline/StrikeOut/Squiggly with
Targetbinding to aTextBlockfor AFM quad derivation), FreeText, Stamp, Square, Circle, Line, Polygon, PolyLine, Ink. Cross-pageTargetreferences resolve through a prepass that records every element's PDF-space coordinates before the walk. - Form widgets —
TextBox,CheckBox,ComboBox,ListBox,Buttonbecome AcroForm fields when tagged withjetsonpdf:Form.FieldName="...". Children of form-tagged controls are skipped so the viewer's default chrome doesn't double-paint. - Page-context markup extensions —
{jetsonpdf:PageNumber}/{jetsonpdf:PageCount}resolve againstJetsonPageContext, which the walker stamps onto each page'sDataContextbefore Measure.Run.Text(not a DP in OpenSilver) uses a sentinel-substitution pass before Measure so layout reflects the final string. - UIElement.Effect (DropShadow / Blur / custom shader) — the subtree is rasterised via
WriteableBitmap.Renderand embedded as a PNG image (descendants are skipped so the baked pixels aren't double-painted). - Base64 images —
{jetsonpdf:Base64Image Data='…'}produces aBitmapImagefrom adata:URI (MIME sniffed from the first base64 chars: PNG / JPEG / GIF).
The two practical differences from the WPF integration:
- Async.
ConvertAsyncis asynchronous because OpenSilver'sBitmapImageloading goes throughdata:/HttpClientand resolves asynchronously. The WPF integration is synchronous because PNG encoding happens in-process on raw pixel buffers. - WriteableBitmap.Render needs a live DOM. Image rasterisation (and the effects-rasterisation path) relies on
WriteableBitmap.Render, which in the Wasm runtime needs the element parented to a live host Canvas. In the Simulator or in unit tests that don't supply a host, rasterisation soft-fails and the image is omitted from the snapshot — layout still completes.
Hosting
OpenSilver's layout is partly DOM-driven; calling Measure/Arrange on a detached element resolves to zero metrics in the Wasm runtime. Production hosts parent the parsed authoring tree to a hidden host Canvas for the duration of the walk:
// In your OpenSilver page's Loaded handler:
OpenSilverTreeWalker.HostContainer = HostCanvas;
After HostContainer is set, the walker attaches each parsed root, runs Measure/Arrange/UpdateLayout, walks the visual tree, and detaches before returning. If HostContainer is left null (Simulator / detached test), the walker falls back to detached layout — some metrics may resolve to zero, but the API still works.
The companion host scaffold lives at ../JetsonPDF.OpenSilver.Sample.Host/: a UserControl-based shell with .Browser (WebAssembly) and .Simulator (WebView2) flavours, plus a [JSInvokable] JS bridge that exposes window.__jetsonpdfConvert(xaml) to Playwright-style callers.
For a runnable example that drives the converter through 15 authoring-XAML demos, see samples/JetsonPDF.OpenSilver.Showcase/.
JPEG + SMask alpha
PDFs can pair a JPEG image with a soft-mask (/SMask) alpha channel. The browser's <img> element decodes the JPEG natively but ignores the external alpha, so the alpha has to be merged into the pixels before the image is handed to the renderer. The WPF adapter does this via JpegBitmapDecoder; OpenSilver targets netstandard2.0 and has no managed JPEG decoder in the BCL — even in .NET 8/9 there is none — so we bridge to the browser's own JPEG decoder via createImageBitmap and an offscreen canvas.
OpenSilverImageDecoders.Jpeg defaults to DefaultBrowserJpegDecoder.Instance, so JPEG+SMask alpha composites correctly out of the box inside any modern browser host (WebAssembly, Edge WebView2, Playwright Chromium). In non-browser hosts (the OpenSilver Simulator before the JS bridge is up, unit tests, server-side conversion) the default decoder catches the missing-Interop exception, emits a one-time stderr warning, and returns null — so the previous alpha-drop pass-through kicks in. No exceptions, no behavior regression.
To swap in a managed decoder (better throughput when a document has many large JPEG+SMask images — the default round-trips ~33 MB of base64 RGB through the JS bridge for a 4K image):
using JetsonPDF.OpenSilver;
OpenSilverImageDecoders.Jpeg = new MyJpegDecoder();
OpenSilverImageDecoders.Jpx = new MyJpxDecoder(); // JPEG 2000, same pattern; no default
internal sealed class MyJpegDecoder : IJpegPixelDecoder
{
public Task<JpegPixelResult?> TryDecodeRgb24Async(byte[] jpegBytes, CancellationToken ct = default)
{
// Wrap your decoder of choice (SkiaSharp, ImageSharp, libjpeg port, …).
// Output: width*height*3 bytes of R,G,B,R,G,B,…. Return null on failure.
return Task.FromResult<JpegPixelResult?>(new JpegPixelResult(rgb, width, height));
}
}
To disable JPEG+SMask compositing entirely (and accept the alpha-drop pass-through):
OpenSilverImageDecoders.Jpeg = null;
JPX (JPEG 2000) has no default decoder because browsers don't decode JP2 portably (Safari only). Register your own if you need it.
Async API note
Because the default decoder bridges through a JS Promise, IJpegPixelDecoder.TryDecodeRgb24Async is async and PdfToXamlConverter.Convert is now PdfToXamlConverter.ConvertAsync(...) → Task<string>. The WPF flavour returns a synchronously-completed Task (no JS bridge involved), so blocking on the result via .GetAwaiter().GetResult() is safe in WPF; in OpenSilver, await it.
Browser-side PDF → TIFF
PdfToTiffBrowserConverter rasterises every page of a PDF to a multipage TIFF without a server round-trip or a native image library — the whole pipeline runs in WebAssembly. It reads the PDF with JetsonPDF.Reader, turns each page into XAML via PdfToXamlConverter, mounts that into a hidden host Panel, snapshots the resulting DOM with html2canvas (fetched from a CDN on first use), reads the RGBA pixels back through getImageData, and encodes the frames through JetsonPDF.Tiff's managed TiffWriter.
using JetsonPDF.OpenSilver;
using JetsonPDF.Tiff;
// HostCanvas is an empty Panel already parented in the live visual tree.
byte[] tiff = await PdfToTiffBrowserConverter.ConvertAsync(
pdfBytes,
HostCanvas,
new TiffWriteOptions { Compression = TiffCompression.Deflate },
progress: new Progress<TiffConversionProgress>(p => StatusText.Text = p.Description));
- The
hostpanel must already be inApplication.Current's visual tree. OpenSilver's layout is DOM-driven; a detached panel resolves to zero metrics and the converter would emit blank pages. The converter sizes the panel per page, mounts the parsed XAML, snapshots, and clears it (restoring the panel's prior state even if an exception unwinds). - Raster images are composited onto the captured canvas directly, because
html2canvascan't reliably snapshot OpenSilver's asynchronously-loaded<Image>elements. - Progress — the optional
IProgress<TiffConversionProgress>sink reports eachTiffConversionStage(Starting→ConvertingXaml/Renderingper page →Encodingper frame →Completed), suitable for driving aProgressBar.
Browsers can't natively display TIFF, so pair this with JetsonPDF.Tiff's TiffImage.Decode + ToDataUri() to show the result in an <Image>. See samples/JetsonPDF.OpenSilver.Showcase/'s TiffViewerPage for a round-trip. For the Windows desktop equivalent, use the separate JetsonPDF.PdfToTiffConverter package.
Architecture
- Imports the shared
JetsonPDF.XamlToPdfConverter.Authoring.projitems— same authoring types (Document,Page,Form, annotation classes,PageContextSentinel, …) as the WPF integration; underOPENSILVER, those types compile into theJetsonPDF.OpenSilver.Authoringnamespace. - Walker (
OpenSilverTreeWalker) is OpenSilver-specific (it usesVisualTreeHelper.GetChildrenCountagainstDependencyObjectbecause Silverlight has noSystem.Windows.Media.Visual, and it has to manage host parenting). - Snapshot types and PDF emission live in
JetsonPDF.XamlToPdfConverter.Core— runtime-neutral.
Targets
netstandard2.0- OpenSilver 3.2+
- Defines
OPENSILVERfor code that conditionally specialises.
NuGet
dotnet add package JetsonPDF.OpenSilver
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 was computed. 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 was computed. 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. |
-
.NETStandard 2.0
- JetsonPDF.Common (>= 1.0.0)
- JetsonPDF.Reader (>= 1.0.0)
- JetsonPDF.Tiff (>= 1.0.0)
- JetsonPDF.Writer (>= 1.0.0)
- JetsonPDF.XamlToPdfConverter.Core (>= 1.0.0)
- Microsoft.Bcl.HashCode (>= 6.0.0)
- OpenSilver (>= 3.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 92 | 5/23/2026 |
| 0.2.0-preview | 93 | 5/23/2026 |
| 0.1.0-preview | 88 | 5/17/2026 |