Skip to main content
.NETStandard2.0

Tesserae.Pdf is a Tesserae (Transpose C# to JavaScript) wrapper around Mozilla's pdf.js, providing a document viewer component, a single-page canvas renderer, and a typed API over the pdf.js display and viewer surfaces.

Published
Aug 21, 2026
1mo ago
Total Downloads
1.7K
1,724

Install

dotnet add package Tesserae.Pdf --version 26.8.4319
ProductVersions
.NET Standard
netstandard2.0

README

Tesserae.Pdf

A Tesserae wrapper around Mozilla's pdf.js, for Transpose C#-to-JavaScript apps.

PdfJs.Viewer()
   .Url("/api/files/42.pdf")
   .FitWidth()
   .OnPageChanged(page => label.Text = $"Page {page} of {viewer.PageCount}")

pdf.js ships with the package - there is nothing to add to your page, no CDN to configure, and no worker path to keep in sync.

What is in it

PdfJs.Viewer() - a scrollable, searchable, linkable document viewer. Pages, links, text selection, form fields, keyboard scrolling and full-text search all work with no further wiring. It draws no toolbar: that is the part that has to look like the rest of your application, so the component exposes the methods a toolbar calls (NextPage, FitWidth, Rotate, Search, ...) and leaves the buttons to you.

PdfJs.PageCanvas() - one page painted into a canvas. A thumbnail, a preview tile, a page in a contact sheet. Give it a URL and it opens its own document; give it a PdfDocument and it borrows one, which is how a rail of thumbnails shares a single document rather than opening twelve.

PdfJs.OpenAsync(source) - a document with nothing on screen, for the things that need no viewer: extracting text, reading metadata and permissions, listing an outline, rendering a page into a canvas of your own.

Alongside those: encrypted documents (OnPassword), embedded JavaScript (EnableScripting), localization through Tesserae's TNT table, typed failures (PdfError.Kind), and SaveAsync for getting a filled form's bytes back out.

The sample gallery has a page per feature.

Getting started

Add the package. Its build copies pdf.js into your app's output under assets/js/pdf, and the components load it from there on first use - nothing is fetched until a viewer mounts.

var viewer = PdfJs.Viewer();

viewer
   .Url("report.pdf")
   .FitWidth()
   .OnDocumentLoaded(document => Console.WriteLine($"{document.PageCount} pages"))
   .OnError(error => ShowMessage(error.Message));

// Give it a height, or a parent that has one: the viewer fills its container and scrolls inside it.
MountToBody(viewer.H(600).WS());

Serving pdf.js from somewhere else - a CDN, a shared static host - is one setting, and it moves the worker and every asset directory with it:

PdfJs.AssetsPath = "https://static.example.com/pdfjs";

Things worth knowing

Give the viewer a height. It fills its container and scrolls inside it, so in a container of no height it renders nothing - which looks like a document that failed to load.

Prefer the fit modes to an explicit zoom. FitWidth() and its siblings are re-applied when the container resizes; Zoom(1.4) is a number and stays one. (pdf.js resolves a fit mode once, into a number, and does not re-resolve it - the component re-applies it for you.)

A component owns what it opened. A viewer releases its document when it is torn down, including the teardown that happens when it leaves the DOM; being re-added rebuilds it and restores the page, zoom, rotation and layout. A document you opened yourself with PdfJs.OpenAsync is yours to release with DestroyAsync.

AnnotationMode.EnableStorage is not "EnableForms and more". In a viewer it makes the form non-interactive, silently - pdf.js tests for exactly EnableForms when deciding whether to build real inputs. EnableForms is the default and the right choice for a viewer; EnableStorage belongs on a page render, where it means "include the values already entered".

Whether a viewer has an annotation editor is decided before it is built. Call AnnotationEditor(AnnotationEditorMode.None) while configuring the component to build the editor layer; afterwards tools switch freely, but a viewer built without it cannot grow one.

Watch for "Setting up fake worker" in the console. It means the worker could not be loaded and pdf.js is parsing on the main thread - documents still render, and the UI freezes while they do.

Localizing pdf.js's own strings

pdf.js puts data-l10n-id attributes on the elements it builds - page landmarks a screen reader announces, alt text on annotation icons, tooltips on the editor's buttons - and expects something to turn them into text. This package answers them through TNT, the same translation table Tesserae itself uses, so a German application gets a German viewer from the dictionary that already translates its own buttons. There is nothing to configure.

There is one gap a package cannot close by itself: your tnt extract scans your source, and these strings live in a NuGet package it never sees. So add the keys below to your translation source (or to whatever merges into TNT.T.SetTranslation). They are the English text of every message pdf.js can ask for, and {0} is TNT's own placeholder convention.

PdfJs.Language tells pdf.js which language it is looking at - which decides text direction, and how dates inside annotations are formatted. L10n(customObject) replaces the bridge entirely, and WithoutOwnLocalization() falls back to pdf.js's built-in English.

The 45 translatable strings

Key
[{0} Annotation]
Add comment
Alt text
Alt text added
Blue
Bottom left corner β€” resize
Bottom middle β€” resize
Bottom right corner β€” resize
Change color
Change drawing color
Change text color
Color choices
Comment
Created automatically: {0}
Drawing added
Drawing editor
Edit alt text
Green
Highlight
Highlight added
Highlight editor
Image added
Image editor
Marked as decorative
Middle left β€” resize
Middle right β€” resize
Missing alt text
Page {0}
Pink
Red
Remove drawing
Remove highlight
Remove image
Remove signature
Remove text
Review alt text
Show comment
Signature added
Signature editor: {0}
Text added
Text Editor
Top left corner β€” resize
Top middle β€” resize
Top right corner β€” resize
Yellow

Requirements

  • .NET SDK 10 and the Transpose compiler.
  • Node, for a build from source: pdf.js is not vendored, it is bundled from the pinned pdfjs-dist npm package on every build.

Licensing

This package is MIT. pdf.js is Apache-2.0, and the bundled distribution carries its license plus the separate licenses of the WebAssembly decoders, the substitute fonts and the ICC profile it ships - all of them in assets/js/pdf/LICENSE.txt and beside the files they cover.

Adoption Assessment

Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.

Compatibility metadata lists 1 target framework; confirm the exact target used by your application.

Related Guides & Workflows

Learn how to evaluate, integrate, or audit this package in your .NET project.