Silent Word DOC Printing with Duplex, Reverse & Pages Range Settings from Blazor
Product JSPrintManager for Blazor Published 07/09/2021 Updated 07/09/2021 Author Neodynamic
Overview
In this walkthrough, you'll learn how to silently print DOC/x files from any Razor page directly to the client printer without displaying any print dialog and by writing pure Blazor code. You'll be able to print DOC/x files to the Default client printer as well as to any other installed printer at the client machine with advanced settings like Duplex, Print in Reverse, and Pages Range!
Client-side Requirements:
- Available for Windows clients only
- Microsoft Word 97+ must be installed at the client machine
- DOC files can be any of these file formats: *.docx, *.docm, *.dotx, *.dotm, *.doc, *.dot, *.rtf, and *.odt
Follow up these steps
- Be sure you install in your dev machine JSPrintManager (JSPM) (Available for Windows, Linux, Raspberry Pi & Mac)
This small app must be installed on each client that will print from your website! - In your Blazor project...
- Add a NuGet reference to the JSPrintManager Razor Component
- Add the JSPrintManager service...
- Add the following statement at the top of your
Startup
file
using Neodynamic.Blazor;
- For Blazor Server
Add the following line in theStartup's ConfigureServices
method
services.AddJSPrintManager();
- For Blazor WebAssembly
Add the following line in theProgram's Main
method
builder.Services.AddJSPrintManager();
- For Blazor Server
- Add the following statement at the top of your
- Add the following statement in the
_Imports.razor
file
@using Neodynamic.Blazor
- Add a new Razor Page and copy/paste the following code. Please read the source code comments to understand the printing logic!
@page "/" @inject JSPrintManager JSPrintManager @using System @using System.IO <div> <strong>JSPM </strong><span>WebSocket Status </span> @if (JSPrintManager.Status == JSPMWSStatus.Open) { <span class="badge badge-success"> <i class="fa fa-check" /> Open </span> } else if (JSPrintManager.Status == JSPMWSStatus.Closed) { <span class="badge badge-danger"> <i class="fa fa-exclamation-circle" /> Closed! </span> <div> <strong>JSPrintManager (JSPM) App</strong> is not installed or not running! <a href="https://neodynamic.com/downloads/jspm" target="_blank">Download JSPM Client App...</a> </div> } else if (JSPrintManager.Status == JSPMWSStatus.Blocked) { <span class="badge badge-warning"> <i class="fa fa-times-circle" /> This Website is Blocked! </span> } else if (JSPrintManager.Status == JSPMWSStatus.WaitingForUserResponse) { <span class="badge badge-warning"> <i class="fa fa-user-circle" /> Waiting for user response... </span> } </div> @if (JSPrintManager.Status == JSPMWSStatus.Open) { @if (JSPrintManager.Printers == null) { <hr /> <div class="spinner-border text-info" role="status"> <span class="sr-only">Please wait...</span> </div> <strong><em>Getting local printers...</em></strong> } else { <div class="row"> <div class="col-md-12"> <h2 class="text-center"> <i class="fa fa-files-o" /> Advanced DOC Printing from Blazor </h2> <hr /> </div> </div> <EditForm Model="@MyCPJ"> <div class="row"> <div class="col-md-6"> <div class="bg-light"> <strong>Print Local DOC File</strong> <br /> <InputFile OnChange="@LoadFiles" class="form-control-file" accept=".doc,.docx" /> <br /> @if (isLoading) { <p><em><small>Loading files...</small></em></p> <div class="progress"> <div class="progress-bar bg-info" role="progressbar" style="width: @(loadingStep)%;" aria-valuenow="@(loadingStep)" aria-valuemin="0" aria-valuemax="100">@(loadingStep)%</div> </div> } </div> </div> <div class="col-md-6"> <div class="bg-light"> <strong>Print DOC File from URL</strong> <br /> <div> <small> <strong>Predefined Sample:</strong> </small> <button type="button" class="btn btn-warning btn-sm" @onclick="@(() => SetFilePath("https://neodynamic.com/temp/Sample-Employee-Handbook.doc"))"> <i class="fa fa-arrow-circle-down" /> Sample-Employee-Handbook.doc </button> </div> <InputText Value="@MyFilePath" ValueExpression="@(() => MyFilePath)" ValueChanged="@SetFilePath" class="form-control form-control-sm text-monospace" /> </div> </div> </div> </EditForm> <div class="row"> <div class="col-md-12"> <EditForm Model="@MyPrinter"> <p>Select an <strong>Installed Printer</strong>:</p> <InputSelect @bind-Value="MyPrinter.PrinterName" class="form-control form-control-sm"> @foreach (var p in JSPrintManager.Printers) { <option value="@p">@p</option> } </InputSelect> </EditForm> </div> </div> <div class="row"> <div class="col-md-3"> <label>Pages Range: [e.g. 1,2,3,10-13]</label> <EditForm Model="@MyDocFile"> <InputText @bind-Value="MyDocFile.PrintRange" class="form-control form-control-sm" /> </EditForm> </div> <div class="col-md-3"> <EditForm Model="@MyDocFile"> <label> Print In Reverse Order <InputCheckbox @bind-Value="MyDocFile.PrintInReverseOrder" /> </label> </EditForm> </div> <div class="col-md-3"> <EditForm Model="@MyDocFile"> <label> Duplex <InputCheckbox @bind-Value="MyDocFile.ManualDuplex" /> </label> </EditForm> </div> </div> <div class="row"> <div class="col-md-12"> <br /> <div class="text-center"> <button class="btn btn-success btn-lg" @onclick="DoPrinting"> <i class="fa fa-print" /> Print Now... </button> </div> </div> </div> } } @code { protected override void OnAfterRender(bool firstRender) { if (firstRender) { // Handle OnGetPrinters event... JSPrintManager.OnGetPrinters += () => { if (JSPrintManager.Printers != null && JSPrintManager.Printers.Length > 0) { // Display installed printers... StateHasChanged(); } else { Console.WriteLine("No printers found..."); } }; // Handle OnStatusChanged event to detect any WSS status change JSPrintManager.OnStatusChanged += () => { StateHasChanged(); // Status = Open means that JSPM Client App is up and running! if (JSPrintManager.Status == JSPMWSStatus.Open) { //Try getting local printers... JSPrintManager.TryGetPrinters(); } }; // Start WebSocket comm JSPrintManager.Start(); } base.OnAfterRender(firstRender); } private ClientPrintJob MyCPJ { get; set; } = new(); private InstalledPrinter MyPrinter { get; set; } = new(); private PrintFileDOC MyDocFile { get; set; } = new(); private void DoPrinting() { // set target printer MyCPJ.ClientPrinter = MyPrinter; // set doc file source... if (FileFromUrl) { MyDocFile.FileContentType = FileSourceType.URL; MyDocFile.FileContent = MyFilePath; MyDocFile.FileName = MyFilePath.Substring(MyFilePath.LastIndexOf('/') + 1); } else { foreach (var fileEntry in loadedFiles) { MyDocFile.FileContentType = FileSourceType.Base64; MyDocFile.FileContent = fileEntry.Value; MyDocFile.FileName = fileEntry.Key; } } // Add the doc file to the print job MyCPJ.Files.Clear(); MyCPJ.Files.Add(MyDocFile); // Send job to the client! JSPrintManager.SendClientPrintJob(MyCPJ); } #region File Source Handling private bool FileFromUrl = false; private string MyFilePath { get; set; } private void SetFilePath(string filePath) { MyFilePath = filePath; FileFromUrl = true; } private Dictionary<string, byte[]> loadedFiles = new(); private int maxAllowedFiles = 1; private int maxSizeFile = 5000000; //5MB private bool isLoading = false; private int loadingStep = 0; private async Task LoadFiles(InputFileChangeEventArgs e) { isLoading = true; loadedFiles.Clear(); loadingStep = 0; var numOfFiles = Math.Min(e.FileCount, maxAllowedFiles); var i = 1; foreach (var file in e.GetMultipleFiles(maxAllowedFiles)) { try { await using MemoryStream ms = new(); await file.OpenReadStream(maxSizeFile).CopyToAsync(ms); loadedFiles.Add(file.Name, ms.ToArray()); loadingStep = (int)(((float)i / (float)numOfFiles) * 100f); StateHasChanged(); } catch (Exception ex) { } i++; } FileFromUrl = false; isLoading = false; } #endregion }
- That's it! Run your website and test it. Click on Print Now... to print the Word file without print dialog. You can print it to the Default client printer or you can get a list of the installed printers available at the client machine.