Skip to content

Latest commit

 

History

120 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Web Print Agent

License Latest release .NET Platform Stars Ko-fi

Windows tray agent (.NET 8) that exposes a wss:// JSON-RPC API on localhost so paired HTTPS web pages can list local printers and silent-print PDFs without going through the browser dialog.

Inspired by tools such as QZ Tray and Dymo Web Service, but kept intentionally minimal and focused on PDF printing.

How it works

+------------------------------------+         +-----------------------------------+
|  Web page (https://app.example.com)|         |  Windows desktop                  |
|                                    |         |                                   |
|  await pa.getLocalPrinters()       | <-----> |  PrintAgent.exe (system tray)     |
|  await pa.print({ pdfBase64, ... })|  wss:// |    Kestrel + self-signed cert     |
|                                    |  127.   |    Origin pairing prompt          |
|  printagent-client.js              |  0.0.1: |    SumatraPDF (silent printing)   |
+------------------------------------+  8443+  +-----------------------------------+
  1. The user installs the agent once. A self-signed certificate is added to the user's Trusted Root store so browsers accept wss://localhost.
  2. A web page imports the bundled TypeScript client and calls connect(). The first time a given origin connects, the agent shows a Windows prompt asking the user to authorize that origin.
  3. Once paired, the page can list installed printers, send PDF jobs, and receive asynchronous events (job.statusChanged, printers.changed).

Repository layout

Folder Purpose
agent/ .NET 8 solution: the tray agent + xUnit test project.
client/ Standalone TypeScript client (no npm publish, copy-paste).
installer/ Velopack build script producing the Windows installer.

Build

Prerequisites

  • Windows 10/11 x64
  • .NET 8 SDK
  • Node.js 20+ (for building the TypeScript client)
  • .NET tool: vpk — auto-installed globally by build.ps1 if not present (dotnet tool install -g vpk)
  • SumatraPDFalready vendored at agent/PrintAgent/Resources/SumatraPDF.exe (3.6.1, ~19 MB, portable 64-bit). It is copied to the publish folder next to PrintAgent.exe, never embedded or extracted at runtime. To bump the version, replace the file and update SUMATRAPDF-NOTICE.txt.

Build the agent

cd agent
dotnet publish PrintAgent/PrintAgent.csproj -c Release -r win-x64

Output: agent/PrintAgent/bin/Release/net8.0-windows/win-x64/publish/ - a self-contained folder (~190 MB, ~390 files) containing PrintAgent.exe, the .NET runtime, and SumatraPDF.exe. Velopack packages the whole folder.

Do not add -p:PublishSingleFile=true. A self-extracting bundle is a packer signature for Defender's ML heuristics, and unsigned builds get flagged as Trojan:Win32/Sabsik.FL.A!ml. See "Antivirus false positives" below.

Run the test suite

cd agent
dotnet test

Build the TypeScript client

cd client
npm install
npm run build

Output: client/dist/printagent-client.js and printagent-client.d.ts. These two files are also committed to the repo so consumers can grab them directly without running the build.

Build the installer

cd installer
./build.ps1 -Version 0.1.0

build.ps1 wipes installer/Output/ before packing, so every build starts from a clean slate.

Output in installer/Output/:

  • PrintAgent-win-Setup.exe - run on the target machine to install (per-user, no elevation required)
  • PrintAgent-win-Portable.zip - no-install variant
  • RELEASES / releases.win.json - Velopack release feed for auto-update
  • PrintAgent-<version>-full.nupkg - full update package

The installer is built with Velopack. The vpk dotnet tool is installed automatically by build.ps1 if not already present.

Releases are full-only (--delta None). Delta generation would diff against whichever older .nupkg files happen to be in Output/, which makes builds depend on local leftovers. Auto-update still works - Velopack falls back to the full package - but each update downloads the whole ~92 MB payload instead of a small patch.

Antivirus false positives

PrintAgent is not code-signed, so Windows Defender's machine-learning heuristics may flag the build as Trojan:Win32/Sabsik.FL.A!ml - the generic bucket for unsigned Windows executables of unknown reputation. Two build choices exist specifically to keep that surface small:

  • No PublishSingleFile. A self-extracting bundle looks like a packed binary. Velopack packages the publish folder anyway, so the flag buys nothing.
  • SumatraPDF.exe is shipped, not extracted. Writing an executable to disk at runtime and spawning it is the "dropper" pattern; shipping it directly also keeps SumatraPDF's own Authenticode signature intact.

What remains is the unsigned Setup.exe and zero SmartScreen reputation. Until the project is signed:

  1. Submit each release to Microsoft as a false positive: https://www.microsoft.com/en-us/wdsi/filesubmission ("Software developer" tab). The correction propagates to all machines, but it is per-hash, so it must be redone every release.
  2. For local development, exclude the build output directories rather than individual files (hashes change on every publish):
Add-MpPreference -ExclusionPath "<repo>\build\agent\PrintAgent\bin"
Add-MpPreference -ExclusionPath "<repo>\build\installer"

The real fix is an Authenticode signature. See the project notes on SignPath Foundation (free for OSS) and Azure Trusted Signing.

Test page

A standalone HTML demo is shipped alongside the client at client/dist/index.html. It connects to a running PrintAgent, lists printers, lets you upload a PDF and watch job events stream in.

cd client/dist
npx http-server -p 8080
# then open http://localhost:8080/

Note: the page must be served over HTTPS (or from a https://*.dev.localhost vhost trusted by your browser) so the wss://127.0.0.1:8443 connection passes the same-origin and mixed-content checks. By default the agent rejects http:// origins; flip AllowInsecureOrigins to true in appsettings.json only for local dev.

Using the client in a web page

Copy client/dist/printagent-client.js and printagent-client.d.ts into your project (for example under src/lib/).

import { PrintAgent } from './lib/printagent-client'

const pa = new PrintAgent()
await pa.connect() // user sees the pairing prompt the first time

const printers = await pa.getLocalPrinters()
const { jobId } = await pa.print({
    printerName: 'HP LaserJet',
    pdfBase64,
    options: { copies: 1, paperSize: 'A4' }
})

pa.on('job.statusChanged', (event) => {
    if (event.jobId === jobId) console.log(event.status)
})

Code signing

Release binaries are signed using free code signing provided by SignPath.io, with a certificate from the SignPath Foundation. See CODE_SIGNING_POLICY.md for the build, signing, roles, and privacy details.

Credits

License

This package is open-sourced software licensed under the MIT license.

Third-party software

PrintAgent embeds a copy of SumatraPDF (3.6.1) for silent PDF printing. SumatraPDF is licensed under the GNU GPL v3.0 or later. PrintAgent invokes it as a separate subprocess, so the two programs form an aggregate under section 5 of the GPL and PrintAgent's MIT license is unaffected. See agent/PrintAgent/Resources/SUMATRAPDF-NOTICE.txt for the full attribution and links to the source code.

Support

If this project helps to increase your productivity, you can give me a cup of coffee :)

Donate

About

Windows agent that allow you to print PDF directly from a web page without print dialog.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages