Skip to content

PdfSpace

Documents & Officev0.5.5-alpha.1 MIT 2

Read, edit and review PDFs — locally, in the browser or on desktop

PdfSpace is a local-first PDF application built in C# with Uno Platform and SkiaSharp. The browser runs a real Uno WebAssembly app with the same document, editing and rendering libraries as the desktop host, and an Acrobat-inspired shell brings together tabs, comments, form preparation, native object and original-text editing, on-device OCR, AES-256 protection, redaction and page organization. No document or password is sent to a processing service.

12reusable, packable libraries
3on-device OCR languages
AES-256password protection in the browser
5distinct save and export operations
PdfSpacewieslawsoltes.github.io/PdfSpace/
PdfSpace running in a browser
Highlights

What makes PdfSpace special

Familiar if you know Acrobat-style PDF editors — but original, local‑first and written end‑to‑end in C#.

Genuine Uno WebAssembly

The browser build is a real Uno/Skia application sharing its engines with the desktop host — not a PDF.js viewer or HTML canvas app.

Review without flattening

Highlights, notes, replies, ink, shapes, stamps and links are written back as native PDF annotation objects.

Prepare and fill forms

Create text, check-box and dropdown fields, fill imported forms, and import or export values as XFDF or JSON.

Edit native objects

Select native text, paths, images and Form groups on the page, then move, resize, align, group, crop or restyle them with one-step undo.

Real original-text edits

Replaces actual PDF text-showing operands instead of covering them with a white rectangle; unsupported cases are rejected.

Local Scan & OCR

Recognize image-only PDFs in English, Polish or German, review words against the scan, and export a searchable PDF.

Protect and redact a copy

Export AES-256 PDFs with separate opening and owner passwords, or build a confirmed image-only redacted copy.

Indexed performance

Indexed page geometry, bounded search indexes and bounded picture/source caches reduce repeated layout and parsing work on large documents.

0reusable libraries
0lines of C# & XAML
0features catalogued
0.NET projects
0CI workflows
0docs
Feature tour

33 capabilities, 7 areas

Read and navigate

  • Multiple document tabs for PDFs and editable workspaces
  • Search and copy text; zoom, pan and Ctrl+wheel zoom around the pointer
  • Continuous, single-page and two-page layouts with virtualized thumbnails
  • Original PDF outlines alongside editable workspace bookmarks
  • Local named destinations and internal links that follow page reorganization

Review and comment

  • Text, highlight, underline, strikethrough, notes and threaded replies
  • Ink, shapes, arrows, stamps and links
  • Move, resize and recolor annotations; resolve comments; undo and redo
  • Supported annotations and replies export as native PDF objects

Forms

  • Create text, check-box and dropdown fields directly on a page
  • Fill imported text fields, check boxes, radio groups and single-select choices
  • Edit tooltips, defaults, read-only/required/multiline flags, font size and maximum length
  • Import/export plain field values as XFDF or versioned JSON in one undoable transaction
  • Check required fields and Tab / Shift+Tab between supported widgets

Edit existing content

  • Replace supported original text, including text inside nested Form XObjects
  • Select, move, resize, rotate, flip, replace, insert or delete native image placements
  • Align, distribute, crop, stack, group and copy/paste objects across documents
  • Edit real path geometry, control points and solid fill/stroke
  • Native opacity, blending, stroke caps/joins, miter and dash editing
  • Shift constrains movement and aspect ratio; Alt resizes from the center

Scan & OCR

  • Local recognition in English, Polish or German at 150, 200 or 300 DPI
  • Recognize image-only PDFs or import PNG/JPEG scans honoring EXIF orientation
  • Review low-confidence words against the original scan and correct them
  • Export a searchable PDF with an invisible Unicode text layer
  • Browser self-hosts pinned Tesseract.js/WASM models; native hosts use Tesseract 5

Organize and export

  • Reorder, rotate, duplicate, delete, crop, combine, extract and split pages
  • Export native PDF or a separately labeled flattened visual PDF
  • Download PNG, text, form data or an editable .pdfspace workspace
  • Browser recovery in IndexedDB; atomic temporary-file recovery on desktop

Protect and redact

  • AES-256 revision 6 output with distinct opening and owner passwords
  • Print, copy and edit permission flags; owner authentication before re-editing
  • Browser encryption runs in an isolated, hash-pinned QPDF WASM worker
  • Raster redaction rebuilds every page as an image with burned-in marks
Integrated playground

Run PdfSpace right here

Open the live WebAssembly build and try every workflow on the included original sample documents — no upload of private files needed. Switch viewports, go fullscreen and tick off the guided tour as you explore.

https://wieslawsoltes.github.io/PdfSpace/
PdfSpace screenshot

Try PdfSpace live

Open the live WebAssembly build and try every workflow on the included original sample documents — no upload of private files needed.

Real C# · Uno WebAssembly · runs locally in your browser
Booting .NET WebAssembly runtime…
Reusable packages

12 libraries you can embed

PdfSpace is assembled from independently reusable libraries, each published on NuGet.org. Hover the graph to trace dependencies; click a node to jump to its package, and open a card for install and usage.

Core 362 lines Layout 671 lines Editing 385 lines Documents 347 lines Rendering.Skia 8,056 lines Skia 371 lines Pdf 3,476 lines Ocr 165 lines Controls 297 lines Viewer 1,168 lines Storage 15 lines Workbench 2,375 lines App 280 lines
Arrows point from a project to what it references. Bars show relative size. ModelEngineI/ORenderingServiceControlsEditorWorkbench
Model 0.5.5-alpha.1

PdfSpace.Core

Workspace, page, annotation, field and geometry models with validated JSON

No UI depsnet10.0
362 lines12 files0 refsUsage
Model 0.5.5-alpha.1

PdfSpace.Layout

Crop/rotation transforms and viewport/page layouts without UI dependencies

No UI depsnet10.0
refs → Core
671 lines8 files1 refsUsage
Engine 0.5.5-alpha.1

PdfSpace.Editing

Transactional editing, shared field state and bounded undo/redo

No UI depsnet10.0
refs → Core
385 lines7 files1 refsUsage
I/O 0.5.5-alpha.1

PdfSpace.Documents

PdfPig import, text extraction, search, page ranges and XFDF/JSON form data

No UI depsnet10.0
refs → Core
nuget → PdfPig
347 lines5 files1 refsUsage
Rendering 0.5.5-alpha.1

PdfSpace.Rendering.Skia

Source-pinned Apache-2.0 PdfPig Skia renderer with image-alpha, dash and miter fixes

SkiaSharpnet10.0
nuget → PdfPig, PdfPig.Filters.Dct.JpegLibrary, PdfPig.Filters.Jbig2.PdfboxJbig2, PdfPig.Filters.Jpx.OpenJpeg, SkiaSharp, SkiaSharp.HarfBuzz
8,056 lines32 files0 refsUsage
Rendering 0.5.5-alpha.1

PdfSpace.Skia

Cached rendering, overlays, visual/PNG export and raster redaction

SkiaSharpnet10.0
refs → Documents, Layout, Rendering.Skia
nuget → SkiaSharp, SkiaSharp.HarfBuzz
371 lines6 files3 refsUsage
I/O 0.5.5-alpha.1

PdfSpace.Pdf

PDFsharp native annotation/form writer, source-text editing and security-provider contract

PDFsharp + SkiaSharpnet10.0
refs → Skia
nuget → PDFsharp
3,476 lines30 files1 refsUsage
Service 0.5.5-alpha.1

PdfSpace.Ocr

Local OCR contracts, raster preparation, bounded TSV decoding and snapshot batches

SkiaSharpnet10.0
refs → Skia
165 lines4 files1 refsUsage
Controls 0.5.5-alpha.1

PdfSpace.Controls

Custom Uno icons, command buttons, tabs, panels, palettes and dialogs

Uno + Skianet10.0-browserwasmnet10.0-desktop
297 lines11 files0 refsUsage
Editor 0.5.5-alpha.1

PdfSpace.Viewer

Embeddable document viewport with annotation editing, form input and thumbnails

Uno + Skianet10.0-browserwasmnet10.0-desktop
refs → Controls, Editing, Skia
1,168 lines9 files3 refsUsage
Service 0.5.5-alpha.1

PdfSpace.Storage

Platform-neutral file, recovery, clipboard and print contracts

No UI depsnet10.0
15 lines1 files0 refsUsage
Workbench 0.5.5-alpha.1

PdfSpace.Workbench

Complete multi-document shell and PDF workflows

Uno + Skianet10.0-browserwasmnet10.0-desktop
refs → Pdf, Viewer, Storage, Ocr
2,375 lines17 files4 refsUsage
Install from NuGet

12 packages on NuGet.org

PdfSpace ships as twelve packages, all versioned together. Eleven are MIT-licensed; PdfSpace.Rendering.Skia is Apache-2.0, a derivative of an upstream renderer (see its subsection). Nine target plain net10.0 and have no UI dependency: PdfSpace.Core, Layout, Editing and Storage are pure .NET, Documents adds PdfPig, Rendering.Skia, Skia and Ocr add SkiaSharp, and Pdf adds PDFsharp. The three Uno Platform packages (Controls, Viewer, Workbench) target net10.0-desktop and net10.0-browserwasm. Symbols are published to nuget.org as .snupkg with SourceLink. The application itself is a thin browser/desktop host and is not packaged; see also the embedding guide.

12packages
0.5.5-alpha.1pre-release · Sep 29, 2026
9plain .NET — no UI framework
Newjust published
bashStart with the foundation package
dotnet add package PdfSpace.Core --prerelease
xmlEvery package — keep only what you need
<PackageReference Include="PdfSpace.Core" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Layout" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Editing" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Storage" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Documents" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Rendering.Skia" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Skia" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Pdf" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Ocr" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Controls" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Viewer" Version="0.5.5-alpha.1" />
<PackageReference Include="PdfSpace.Workbench" Version="0.5.5-alpha.1" />
xmlCentral package management
<!-- Directory.Packages.props -->
<ItemGroup>
  <PackageVersion Include="PdfSpace.Core" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Layout" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Editing" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Storage" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Documents" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Rendering.Skia" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Skia" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Pdf" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Ocr" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Controls" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Viewer" Version="0.5.5-alpha.1" />
  <PackageVersion Include="PdfSpace.Workbench" Version="0.5.5-alpha.1" />
</ItemGroup>

Published with NuGet Trusted Publishing from GitHub Actions, with symbols (.snupkg) and SourceLink. Release v0.5.5-alpha.1 ↗

Dependencies (from project references): Core ← Layout, Core ← Editing, Core ← Documents; Documents + Layout + Rendering.Skia ← Skia ← Pdf, Skia ← Ocr; Controls + Editing + Skia ← Viewer; Pdf + Viewer + Storage + Ocr ← Workbench. Storage, Controls and Rendering.Skia have no PdfSpace dependencies.

PdfSpace.Core

ModelNo UI depsnet10.0v0.5.5-alpha.1

The immutable document model shared by every package: a PdfWorkspace holds the original source bytes plus per-page state (rotation, crop, bookmarks, annotations with replies, form fields and OCR layers), so edits stay reversible until export. Includes validated .pdfspace JSON and page selection/composition helpers. No dependencies and no UI.

bashInstall
dotnet add package PdfSpace.Core --prerelease
Depends onno other PdfSpace packageUsed byLayoutEditingDocuments

Key types

  • PdfWorkspace – Sources, Pages, UpdatePage, IsSensitive, AnnotationCount, FieldCount
  • PdfPageState, PdfSource – page geometry/state and original PDF bytes
  • Annotation / AnnotationKind, PdfFormFieldState / PdfFieldKind – review and form models
  • WorkspaceJson – Save, Load (bounded, validated), Validate
  • WorkspacePages.Select, WorkspaceComposition.Append, PdfFormData.FromWorkspace
  • RectD, PointD – page-space geometry in PDF points

Basic usage

csharpUsage
using PdfSpace.Core;

var page = new PdfPageState { Width = 612, Height = 792 };
var note = new Annotation { Kind = AnnotationKind.Note, Bounds = new RectD(72, 72, 24, 24), Text = "Check totals" };
var workspace = new PdfWorkspace { Title = "Review.pdf", Pages = [page with { Annotations = [note] }] };

workspace = workspace.UpdatePage(page.Id, p => p with { Rotation = 90, Bookmark = "Summary" });
var firstPageOnly = WorkspacePages.Select(workspace, [0]);

string json = WorkspaceJson.Save(workspace);   // .pdfspace workspace
var restored = WorkspaceJson.Load(json);       // bounded + validated
Console.WriteLine($"{restored.Pages.Length} page(s), {restored.AnnotationCount} annotation(s)");

PdfSpace.Layout

ModelNo UI depsnet10.0v0.5.5-alpha.1

Renderer-independent page geometry: page ↔ display transforms under crop and rotation, an indexed continuous/single/two-page layout with visible-page queries and hit testing, a spatial bounds index for picking dense pages, object snapping (move, resize, points) and selection transforms. Depends on PdfSpace.Core; no UI.

bashInstall
dotnet add package PdfSpace.Layout --prerelease
Depends onCoreUsed bySkia

Key types

  • PageLayoutIndex – Visible, Place, HitTest, NearestPage, TotalHeight for PageLayoutMode
  • PageGeometry – ToDisplay, ToPage, DisplayBounds; PagePlacement.ToPage/ToScreen
  • SpatialBoundsIndex – Query and HitTest over many rectangles
  • ObjectSnapIndex – Snap, SnapResize, SnapPoint with guides
  • SelectionTransform, SelectionRotation – constrained move/resize/create and rotation deltas

Basic usage

csharpUsage
using PdfSpace.Core;
using PdfSpace.Layout;

var pages = Enumerable.Range(0, 50).Select(_ => new PdfPageState()).ToArray();
var index = new PageLayoutIndex(pages, PageLayoutMode.TwoPage);
double width = 1280, zoom = 1.25, scroll = 0, pan = 0;

foreach (PagePlacement placement in index.Visible(width, height: 900, zoom, scroll, pan))
    Console.WriteLine($"page {placement.Index + 1} at {placement.Bounds}");

var screen = new PointD(400, 300);
int hit = index.HitTest(screen, width, zoom, scroll, pan);
if (hit >= 0)
{
    PointD pagePoint = index.Place(hit, width, zoom, scroll, pan).ToPage(pages[hit], screen, zoom);
    Console.WriteLine($"page {hit + 1}, PDF point {pagePoint}");
}
RectD rotatedBox = PageGeometry.DisplayBounds(pages[0] with { Rotation = 90 }, new RectD(72, 72, 200, 50));

PdfSpace.Editing

EngineNo UI depsnet10.0v0.5.5-alpha.1

EditorSession applies every change as an immutable workspace transaction with bounded undo/redo (entry count and retained source bytes): annotations and replies, page rotate/crop/insert/duplicate/delete/move/combine, form-field creation, filling and validated XFDF/JSON imports, and OCR corrections. Custom commands go through Execute. Depends on PdfSpace.Core; no UI.

bashInstall
dotnet add package PdfSpace.Editing --prerelease
Depends onCoreUsed byViewer

Key types

  • EditorSession – Document, CurrentPage, Execute(label, command), Undo/Redo, IsDirty, Changed
  • Annotations and pages – AddAnnotation, UpdateAnnotation, Reply, RotatePage, CropPage, MovePage, Combine
  • Forms – AddField, UpdateField, SetFieldValue, PrepareFormDataImport / ApplyFormDataImport
  • EditorHistoryOptions – MaximumEntries, MaximumSourceBytes; ClearHistory()

Basic usage

csharpUsage
using PdfSpace.Core;
using PdfSpace.Editing;

var session = new EditorSession(new PdfWorkspace(), new EditorHistoryOptions { MaximumEntries = 50 });
session.Changed += (_, _) => Console.WriteLine($"{session.UndoLabel} (dirty: {session.IsDirty})");

session.AddAnnotation(new Annotation
{
    Kind = AnnotationKind.Rectangle,
    Bounds = new RectD(72, 120, 180, 64),
    Color = 0xFF1473E6,
    StrokeWidth = 2
});
session.RotatePage();
session.DuplicatePage();
session.Execute("Bookmark pages", doc => doc with
{
    Pages = doc.Pages.Select((p, i) => p with { Bookmark = $"Page {i + 1}" }).ToArray()
});
session.Undo();
File.WriteAllText("review.pdfspace", WorkspaceJson.Save(session.Document));

PdfSpace.Storage

ServiceNo UI depsnet10.0v0.5.5-alpha.1

The host contract the workbench uses for everything platform-specific: opening documents, images and form data, saving/downloading, local recovery, clipboard text and printing. Implement it once per platform (the app ships browser and desktop implementations). No dependencies and no UI.

bashInstall
dotnet add package PdfSpace.Storage --prerelease
Depends onno other PdfSpace packageUsed byWorkbench

Key types

  • IWorkspaceStorage – OpenAsync, SaveAsync, Read/Write/ClearRecoveryAsync, CopyTextAsync, PrintAsync
  • Optional members with defaults – OpenImageAsync, OpenFormDataAsync
  • WorkspaceFile – name + bytes

Basic usage

csharpUsage
using PdfSpace.Storage;

public sealed class FolderStorage(string root) : IWorkspaceStorage
{
    private string Recovery => Path.Combine(root, "recovery.pdfspace");
    public Task<WorkspaceFile?> OpenAsync() => Task.FromResult<WorkspaceFile?>(null); // show a file picker here
    public Task SaveAsync(string name, byte[] bytes, string contentType) => File.WriteAllBytesAsync(Path.Combine(root, name), bytes);
    public async Task<string?> ReadRecoveryAsync() => File.Exists(Recovery) ? await File.ReadAllTextAsync(Recovery) : null;
    public Task WriteRecoveryAsync(string workspace) => File.WriteAllTextAsync(Recovery, workspace);
    public Task ClearRecoveryAsync() { File.Delete(Recovery); return Task.CompletedTask; }
    public Task CopyTextAsync(string text) => Task.CompletedTask;          // host clipboard
    public Task PrintAsync(string name, byte[] pdf) => Task.CompletedTask; // host print dialog
}

PdfSpace.Documents

I/ONo UI depsnet10.0v0.5.5-alpha.1

Reads PDFs into a PdfWorkspace with PdfPig: page sizes, words with bounds, full-text extraction and search (including a byte-bounded per-document index), one-based page-range parsing, and plain-field form data as XFDF or versioned JSON. Depends on PdfSpace.Core and PdfPig; no UI.

bashInstall
dotnet add package PdfSpace.Documents --prerelease
Depends onCoreUsed bySkia

Key types

  • PdfReader – Open(bytes, name), Words, Find, ExtractText
  • PdfSearchIndex – bounded cached search with Find(document, query, matchCase, wholeWord)
  • PdfTextReader – reusable per-workspace word reader
  • PageRange.Parse("1-3, 5", pageCount) – zero-based indices
  • PdfFormDataCodec – Read, WriteXfdf, WriteJson

Basic usage

csharpUsage
using PdfSpace.Core;
using PdfSpace.Documents;

var workspace = PdfReader.Open(File.ReadAllBytes("report.pdf"), "report.pdf");
Console.WriteLine(PdfReader.ExtractText(workspace));

var search = new PdfSearchIndex();
foreach (SearchResult hit in search.Find(workspace, "revenue", wholeWord: true).Take(10))
    Console.WriteLine($"p.{hit.PageIndex + 1}: {hit.Text} at {hit.Bounds}");

int[] pages = PageRange.Parse("1-3, 5", workspace.Pages.Length); // zero-based indices
File.WriteAllBytes("form.xfdf", PdfFormDataCodec.WriteXfdf(PdfFormData.FromWorkspace(workspace)));
PdfFormData answers = PdfFormDataCodec.Read(File.ReadAllBytes("answers.json"), "answers.json");

PdfSpace.Rendering.Skia

RenderingSkiaSharpnet10.0v0.5.5-alpha.1Apache-2.0

License: Apache-2.0 (not MIT). This package is a derivative work: a source-pinned adaptation of BobLd/PdfPig.Rendering.Skia 0.1.16.4, moved into the isolated PdfSpace.Rendering.Skia namespace, with fixes for image alpha, odd-length dash patterns and miter limits. The package retains the upstream LICENSE.txt and NOTICE.txt plus UPSTREAM.json (original source hashes) and ADAPTATIONS.md (every modification); keep those notices when you redistribute it. Use it directly to rasterize PDF pages with PdfPig and SkiaSharp; PdfSpace.Skia references it for you. It depends on PdfPig (with its DCT, JBIG2 and JPX filter packages), SkiaSharp and SkiaSharp.HarfBuzz; no UI.

bashInstall
dotnet add package PdfSpace.Rendering.Skia --prerelease
Depends onno other PdfSpace packageUsed bySkia

Key types

  • PdfPigExtensions – AddSkiaPageFactory, GetPageSize, GetPageAsSKPicture, GetPageAsSKBitmap, GetPageAsPng
  • SkiaRenderingParsingOptions.Instance – lenient PdfPig parsing with the image filter provider
  • PdfPageSize – Width, Height, PageNumber
  • SkiaPageFactory, SkiaRenderingFilterProvider – the PdfPig factory/filter plumbing

Basic usage

csharpUsage
using PdfSpace.Rendering.Skia;
using SkiaSharp;
using UglyToad.PdfPig;

using var document = PdfDocument.Open(File.ReadAllBytes("input.pdf"), SkiaRenderingParsingOptions.Instance);
document.AddSkiaPageFactory();

for (var page = 1; page <= document.NumberOfPages; page++)
{
    PdfPageSize size = document.GetPageSize(page);
    using var png = document.GetPageAsPng(page, scale: 2);
    File.WriteAllBytes($"page-{page}.png", png.ToArray());
    Console.WriteLine($"page {page}: {size.Width} x {size.Height} pt");
}
using SKPicture picture = document.GetPageAsSKPicture(1); // replay onto any SKCanvas

PdfSpace.Skia

RenderingSkiaSharpnet10.0v0.5.5-alpha.1Apache-2.0

Draws workspace pages: cached source pictures from the Apache-2.0 renderer, then crop/rotation, annotations and form widgets on top. Exports PNG pages and an explicitly flattened visual PDF, rebuilds image-only redacted copies, imports PNG/JPEG scans as pages, and generates the original sample documents. For native annotations and interactive forms use PdfSpace.Pdf instead. Depends on PdfSpace.Documents, PdfSpace.Layout, PdfSpace.Rendering.Skia and SkiaSharp; no UI framework.

bashInstall
dotnet add package PdfSpace.Skia --prerelease

Key types

  • PdfRenderer – DrawPage, ExportPng, ExportPdf (flattened), Typeface, CacheCapacity
  • RasterRedactor.Export – image-only reconstruction with burned-in redaction marks
  • PdfImageImporter – Open a PNG/JPEG as a one-page workspace
  • AnnotationPainter, FormFieldPainter – overlay drawing on any SKCanvas
  • SampleDocument.Create(typeface) – original synthetic demo workspace

Basic usage

csharpUsage
using PdfSpace.Documents;
using PdfSpace.Skia;
using SkiaSharp;

var workspace = PdfReader.Open(File.ReadAllBytes("input.pdf"), "input.pdf");
using var typeface = SKTypeface.FromFile("fonts/Inter.ttf");                 // annotation fallback text
using var renderer = new PdfRenderer { CacheCapacity = 8, Typeface = typeface }; // disposed before the typeface

File.WriteAllBytes("first-page.png", renderer.ExportPng(workspace, pageIndex: 0, scale: 2));
File.WriteAllBytes("flattened.pdf", renderer.ExportPdf(workspace));              // visual copy, not native
File.WriteAllBytes("redacted.pdf", RasterRedactor.Export(workspace, renderer));  // image-only rebuild

using var surface = SKSurface.Create(new SKImageInfo(612, 792));
renderer.DrawPage(surface.Canvas, workspace, workspace.Pages[0]);

PdfSpace.Pdf

I/OPDFsharp + SkiaSharpnet10.0v0.5.5-alpha.1

Structured PDF output and native editing with PDFsharp: save workspaces with real annotation and AcroForm objects, edit supported original text runs, read/move/replace native images, discover and transform native page objects (text, paths, images, shadings, Form groups), read outlines, inspect documents, and encrypt/unlock with AES-256 through IPdfSecurityProvider. Depends on PdfSpace.Skia and PDFsharp; no UI.

bashInstall
dotnet add package PdfSpace.Pdf --prerelease
Depends onSkiaUsed byWorkbench

Key types

  • PdfDocumentEngine – Open (with optional password), Save → PdfWriteResult, Inspect, CanPreserveCatalog
  • PdfTextEditor – Read, Replace, ReplaceWithFont; PdfImageEditor – native image occurrences
  • PdfObjectEditor – Read, Transform, Align, Distribute, Group, Copy/Paste, SetAppearance
  • IPdfSecurityProvider / NativePdfSecurityProvider – EncryptAsync, UnlockAsync; PdfProtectionOptions
  • PdfNavigation.ReadBookmarks, PdfAffineMatrix

Basic usage

csharpUsage
using PdfSpace.Pdf;
using SkiaSharp;

using var typeface = SKTypeface.FromFile("fonts/Inter.ttf"); // font for new annotation/form text
var workspace = PdfDocumentEngine.Open(File.ReadAllBytes("input.pdf"), "input.pdf");

PdfTextRun run = PdfTextEditor.Read(workspace, pageIndex: 0).First(r => r.Editable);
workspace = PdfTextEditor.Replace(workspace, run, "Updated heading");

var paths = PdfObjectEditor.Read(workspace, 0).Where(o => o.Kind == PdfPageObjectKind.Path && o.Editable).ToArray();
workspace = PdfObjectEditor.Transform(workspace, paths, PdfAffineMatrix.Translate(12, 6));

PdfWriteResult result = PdfDocumentEngine.Save(workspace, typeface); // native annotations + AcroForms
File.WriteAllBytes("native-copy.pdf", result.Bytes);
foreach (var warning in result.Warnings) Console.WriteLine(warning);

IPdfSecurityProvider security = new NativePdfSecurityProvider(); // desktop; not for AES on browserwasm
byte[] encrypted = await security.EncryptAsync(result.Bytes,
    new PdfProtectionOptions(openingPassword, ownerPassword, AllowCopy: false));

PdfSpace.Ocr

ServiceSkiaSharpnet10.0v0.5.5-alpha.1

Local OCR orchestration: rasterize selected pages, send them to an IOcrEngine, decode bounded Tesseract TSV into word boxes, and apply the result as one undoable OCR layer that PdfSpace.Pdf exports as an invisible, searchable text layer. TesseractProcessEngine runs an installed Tesseract 5 executable; browser hosts inject their own engine. Nothing is sent to a service. Depends on PdfSpace.Skia; no UI.

bashInstall
dotnet add package PdfSpace.Ocr --prerelease
Depends onSkiaUsed byWorkbench

Key types

  • OcrBatch – RecognizeAsync(snapshot, renderer, engine, pages, language, dpi, progress, token), Apply, WordCount
  • IOcrEngine – RecognizeTsvAsync(image, language, token); TesseractProcessEngine, UnavailableOcrEngine
  • TesseractTsv.Decode – bounded TSV → PdfOcrLayer
  • OcrImage, OcrProgress

Basic usage

csharpUsage
using PdfSpace.Ocr;
using PdfSpace.Skia;

var scan = PdfImageImporter.Open(File.ReadAllBytes("scan.png"), "scan.png"); // PNG/JPEG → one-page workspace
using var renderer = new PdfRenderer();
IOcrEngine engine = new TesseractProcessEngine(); // requires Tesseract 5 on PATH

var progress = new Progress<OcrProgress>(p => Console.WriteLine($"{p.Completed}/{p.Total} {p.Stage}"));
OcrBatch batch = await OcrBatch.RecognizeAsync(scan, renderer, engine, pages: [0], language: "eng", progress: progress);
var searchable = batch.Apply(scan); // adds the recognized OCR layer
Console.WriteLine($"{batch.WordCount} words on {batch.RecognizedPages} page(s)");

PdfSpace.Controls

ControlsUno + Skianet10.0-browserwasmnet10.0-desktopv0.5.5-alpha.1

Original, Skia-drawn Uno building blocks for PDF tools: vector icons, command buttons, document tabs, tool panels, a color palette, an in-window dialog host (prompt/secret/confirm) and styled text fields, plus PdfTheme helpers and PdfResources styles. Compose them into your own shell without the workbench. Depends only on Uno Platform (Skia renderer).

bashInstall
dotnet add package PdfSpace.Controls --prerelease
Depends onno other PdfSpace packageUsed byViewer

Key types

  • PdfIcon / PdfIconKind, PdfCommandButton, PdfTextField
  • PdfToolPanel – Heading, Description, Add(label, icon, action), Items
  • PdfDocumentTab, PdfColorPalette (ColorChanged)
  • PdfDialogHost – PromptAsync, SecretAsync, ConfirmAsync
  • PdfTheme, PdfResources, PdfInputFocus.ActivateWhenLoaded

Basic usage

csharpUsage
using Microsoft.UI.Xaml.Controls;
using PdfSpace.Controls;

var status = PdfTheme.Text("Ready");
var panel = new PdfToolPanel("Review");
panel.Heading("Annotate");
panel.Add("Highlight", PdfIconKind.Highlight, () => status.Text = "Highlight");
panel.Add("Add note", PdfIconKind.Comment, () => status.Text = "Note");
var palette = new PdfColorPalette();
palette.ColorChanged += argb => status.Text = $"#{argb:X8}";
panel.Items.Children.Add(palette);
panel.Items.Children.Add(status);

var dialogs = new PdfDialogHost();
var root = new Grid { Children = { panel, dialogs } };
window.Content = root;
string? title = await dialogs.PromptAsync("Rename", "New document title", initial: "Report.pdf");

PdfSpace.Viewer

EditorUno + Skianet10.0-browserwasmnet10.0-desktopv0.5.5-alpha.1

The embeddable document viewport: Skia-rendered pages with continuous/single/two-page layouts, zoom/pan, text selection, direct annotation creation and editing for the session's current tool, in-place form filling, search highlighting and native object/image manipulation surfaces, plus virtualized PdfThumbnailView. Navigation and zoom are view operations; document changes belong to EditorSession. Depends on PdfSpace.Controls, PdfSpace.Editing, PdfSpace.Skia and Uno Platform.

bashInstall
dotnet add package PdfSpace.Viewer --prerelease

Key types

  • PdfViewport – PdfViewport(session), Renderer, Navigate, FitPage, ZoomTo, SetLayout, HighlightSearch
  • PdfViewport events – StatusChanged, NoteRequested, FieldRequested, LinkRequested, ContextRequested
  • PdfThumbnailView – PdfThumbnailView(viewport), PageActivated, OrganizeMode
  • NativeObjectTarget, NativeImageTarget – detached geometry the host supplies for object editing

Basic usage

csharpUsage
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using PdfSpace.Core;
using PdfSpace.Documents;
using PdfSpace.Editing;
using PdfSpace.Viewer;

var session = new EditorSession(PdfReader.Open(pdfBytes, "input.pdf"));
var viewport = new PdfViewport(session);
var thumbnails = new PdfThumbnailView(viewport) { Width = 180 };

session.SetTool(PdfTool.Highlight);
viewport.NoteRequested += (pageIndex, point) => session.AddAnnotation(
    new Annotation { Kind = AnnotationKind.Note, Bounds = new RectD(point.X, point.Y, 24, 24), Text = "New note" }, pageIndex);
viewport.Loaded += (_, _) => viewport.FitPage(); // needs a nonzero arranged size

var root = new Grid { ColumnDefinitions = { new ColumnDefinition { Width = GridLength.Auto }, new ColumnDefinition() } };
Grid.SetColumn(viewport, 1);
root.Children.Add(thumbnails);
root.Children.Add(viewport);
window.Content = root;
window.Closed += (_, _) => viewport.Dispose();

PdfSpace.Workbench

WorkbenchUno + Skianet10.0-browserwasmnet10.0-desktopv0.5.5-alpha.1

The complete Acrobat-style shell used by the app: document tabs, quick tools, comments, form preparation, original-text and native object editing, page organization, search, export, protection, redaction, OCR and recovery. Platform services come from IWorkspaceStorage; security and OCR providers are optional constructor arguments. The host owns the injected typeface; the workbench owns the viewports it creates. Depends on PdfSpace.Pdf, PdfSpace.Viewer, PdfSpace.Storage, PdfSpace.Ocr and Uno Platform.

bashInstall
dotnet add package PdfSpace.Workbench --prerelease

Key types

  • PdfWorkbench – PdfWorkbench(initial, storage, typeface, security?, ocr?), AddDocument, OpenFileAsync, OfferRecoveryAsync
  • State – Session, Viewport, DocumentCount, HasUnsavedChanges, StateChanged, ShowStatus

Basic usage

csharpUsage
using PdfSpace.Ocr;
using PdfSpace.Pdf;
using PdfSpace.Skia;
using PdfSpace.Storage;
using PdfSpace.Workbench;
using SkiaSharp;

using var fontData = SKData.CreateCopy(fontBytes);
var typeface = SKTypeface.FromData(fontData);          // host-owned UI/annotation font
IWorkspaceStorage storage = new FolderStorage(appData); // see PdfSpace.Storage above

var workbench = new PdfWorkbench(SampleDocument.Create(typeface), storage, typeface,
    security: new NativePdfSecurityProvider(), ocr: new TesseractProcessEngine()); // desktop providers
window.Content = workbench;
await workbench.OfferRecoveryAsync();
window.Closed += (_, _) => { workbench.Dispose(); typeface.Dispose(); };
Architecture

From engine to workbench

Layers build bottom‑up: each one depends only on those beneath it.

L1Model and geometry

Immutable workspace, annotation and field models, layout transforms and transactional undo — free of Uno, rendering and storage.

PdfSpace.CorePdfSpace.LayoutPdfSpace.Editing
L2Parsing and rendering

PdfPig import and search feed a source-pinned Skia renderer with cached pictures, overlays and visual export.

PdfSpace.DocumentsPdfSpace.Rendering.SkiaPdfSpace.Skia
L3PDF writing and recognition

A PDFsharp-backed structured writer, pluggable security provider and local OCR pipeline.

PdfSpace.PdfPdfSpace.Ocr
L4Uno controls and viewer

Custom Uno controls and an embeddable Skia viewport for pointer editing, forms and thumbnails.

PdfSpace.ControlsPdfSpace.Viewer
L5Workbench

The multi-document shell composes the workflows; hosts inject browser or desktop storage contracts.

PdfSpace.WorkbenchPdfSpace.Storage
Use it in code

Embed, script, extend

bashBuild and run
git clone https://github.com/wieslawsoltes/PdfSpace.git
cd PdfSpace
python3 scripts/fetch-assets.py
dotnet workload install wasm-tools

# Native engine and PDF round-trip tests.
dotnet run --project tests/PdfSpace.Tests -c Release

# Desktop host.
dotnet run --project src/PdfSpace.App -f net10.0-desktop -p:PdfSpaceDesktopOnly=true

# Browser publish and static-root collection.
dotnet publish src/PdfSpace.App -f net10.0-browserwasm -c Release \
  -o artifacts/publish -p:WasmShellWebAppBasePath=/PdfSpace/
python3 scripts/collect-site.py artifacts/publish artifacts/site
python3 scripts/serve-site.py --directory artifacts/site --port 4173

File formats

.pdfread/write
.pdfspace (editable workspace)read/write
Encrypted .pdf (AES-256)read/write
Flattened visual .pdfexport
Redacted image-only .pdfexport
XFDF form dataread/write
JSON form dataread/write
PNG / JPEG (scans and images)import
PNG (page)export
Textexport

Keyboard essentials

Open PDF or workspaceCtrl / Cmd O
New blank PDFCtrl / Cmd N
Save editable workspaceCtrl / Cmd S
Export visual PDFCtrl / Cmd Shift S
FindCtrl / Cmd F
Open printable PDFCtrl / Cmd P
Undo / redoCtrl / Cmd Z / Ctrl / Cmd Shift Z
Fit page / 100%Ctrl / Cmd 0 / Ctrl / Cmd 1
Select / hand / text / ink toolV / H / T / D
Previous / next pagePage Up / Page Down
Nudge selected annotationArrow keys
Cancel gesture or inline text editEscape
Build & CI

Continuously verified

Each push runs through GitHub Actions. The public browser build is deployed only from successful pipelines.

Toolchain

.NET SDK10.0.401
Uno SDK6.7.30
Target frameworksnet10.0-browserwasm · net10.0-desktop · net10.0

Third‑party NuGet packages

PdfPig0.1.16
PdfPig.Filters.Dct.JpegLibrary0.1.16.1
PdfPig.Filters.Jbig2.PdfboxJbig20.1.16.1
PdfPig.Filters.Jpx.OpenJpeg0.1.16
PDFsharp6.2.4
SkiaSharp3.119.4
SkiaSharp.HarfBuzz3.119.4
bashClone & run the browser build locally
git clone https://github.com/wieslawsoltes/PdfSpace.git
cd PdfSpace
dotnet workload install wasm-tools
dotnet run --project src/PdfSpace.App -f net10.0-browserwasm
Honest boundaries

What it doesn’t claim

The project documents its limits as carefully as its features.

  • Functional PDF workflows, not complete or pixel-identical Adobe Acrobat compatibility
  • Export PDF rewrites the document; it is not an incremental or guaranteed lossless save
  • No XFA, form scripts/calculations, certificate signatures, full tagging/compliance or Office conversion
  • Text editing has no paragraph reflow or complex-script shaping; unsupported encodings are rejected
  • Redacted output is image-only; .pdfspace workspaces are unencrypted and keep the full original