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.
Install
| Product | Versions |
|---|---|
| .NET Standard |
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-distnpm 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.
Related Guides & Workflows
Learn how to evaluate, integrate, or audit this package in your .NET project.