How to Generate Password Protected PDF in ASP.NET Core MVC

How to Generate Password Protected PDF in ASP.NET Core MVC

In this article, I will discuss how to generate a Password-protected PDF in an ASP.NET Core MVC application with examples. Please read our previous article discussing How to Generate a PDF in ASP.NET Core MVC Application with Examples. 

What is a Password-Protected PDF?

A Password-Protected PDF is a PDF file that is encrypted so that it cannot be opened, copied, or modified without providing the correct password. This is especially important when the PDF contains sensitive or confidential information. Typically, there are two types of passwords used:

  • User Password: This is required to open the PDF document.
  • Owner Password: This allows certain permissions on the PDF, like printing, copying, or editing, while keeping the document encrypted for general users.

By encrypting the PDF, we ensure that only individuals with the correct password can view or perform certain actions on the PDF.

Why Do We Need Password-Protected PDFs in Web Applications?

In many business scenarios, especially in enterprise or secure environments, sensitive documents like payslips, contracts, reports, or other confidential documents are often transmitted digitally. By encrypting these files with a password:

  • Data Privacy and Confidentiality are maintained: Sensitive documents (payslips, financial statements, reports, etc.) must be protected from unauthorized access. Only the intended recipient with the correct password can access the content.
  • Data is Secured: Even if an email or PDF is intercepted or accessed by an unintended recipient, the document remains unreadable without the password.
  • Access control is enforced. It allows application owners to define user permissions (e.g., printing or editing) and secure the content with a unique user password. That means the recipients cannot modify or print documents unless explicitly allowed.
Real-time Application: Secure Payslip Delivery System

The system is designed to generate, encrypt, and distribute employee payslips securely. Employees receive their monthly payslips via email as password-protected PDFs. The encryption is based on their unique identifiers (e.g., PAN number), ensuring that only the intended recipient can open the file. Additionally, employees can download a copy of their payslip (still password protected) from the portal. This enhances security, reduces manual paperwork, and streamlines the payslip distribution process.

Key Requirements

The following are the Key Requirements and Steps we follow to Implement the application.

Database Design (EF Core Code First):

The database should store employee information such as EmployeeID, FullName, Email, PANNumber, SalaryDetails, PayslipMonthYear, and other necessary fields. Using EF Core, we can define models and relationships.

PDF Generation:

For each employee, a payslip PDF is generated. The file contains:

  • Employee details (Name, PAN, Designation, etc.)
  • Earnings and Deductions
  • Total Payable Amount
  • Month and Year of the Payslip
PDF Encryption:

The PDF should be encrypted with:

  • User Password: Set to the employee’s PAN.
  • Owner Password: Set to a secure admin-level password (for internal purposes, if needed).
Email Delivery:

Once the PDF is generated and encrypted, it is attached to an email and sent to the employee’s registered email address. The email body provides instructions on how to open the PDF (e.g., using their PAN as the password).

Download PDFs:

Provide an interface where employees can securely download their passwords (password-protected PDFs) in case they need a reminder. This process would involve generating another PDF with their credentials and encrypting it before making it available for download.

Project Flow:

Before developing the application, let’s first understand the flow and requirements. The following is the page we will develop.

How to Generate Password Protected PDF in ASP.NET Core MVC

Now, when we click on the Send Last Month’s Payslips to All button, it will send the last month’s payslip to all the Employees. We have also provided one button to Download the PDF. Now, when you click on the PDF, it will ask you to enter the password, as shown in the image below.

How to Generate Password Protected PDF in ASP.NET Core MVC

Once you enter the valid password (in our example, the employee’s associated PAN number), the PDF will open, as shown in the image below.

Why Do We Need Password-Protected PDFs in Web Applications?

Create ASP.NET Core MVC Project and Installing Required Packages

Let’s proceed and implement this example step by step. We will use the following technology stack to implement this application.

  • ASP.NET Core MVC: This is used to build the web application.
  • Entity Framework Core (Code First): For database modeling and interaction.
  • SQL Server: As the database backend.
  • iText7 Library: For PDF generation and encryption.
  • SMTP (or a similar email service): For sending emails with PDF attachments.

Open Visual Studio and create a new ASP.NET Core Web App (Model-View-Controller) project. Name the project SecurePayslipApp. Then, we need to install the following NuGet packages.

  • Microsoft.EntityFrameworkCore.SqlServer: Adds SQL Server support for Entity Framework Core, enabling the app to interact with an SQL Server database.
  • Microsoft.EntityFrameworkCore.Tools: This component provides tools for creating and applying migrations, making it easier to maintain database schema changes.
  • itext7: A library for creating and manipulating PDF documents. It’s used here to generate password-protected payslip PDFs.
  • itext7.bouncy-castle-adapter: This provides cryptographic capabilities (via Bouncy Castle) for the iText7 library, enabling PDF encryption and digital signatures.

Please install the above packages using the Package Manager Console in Visual Studio by executing the following commands:

  • Install-Package Microsoft.EntityFrameworkCore.SqlServer
  • Install-Package Microsoft.EntityFrameworkCore.Tools
  • Install-Package itext7
  • Install-Package itext7.bouncy-castle-adapter
Creating Models

Models represent the application’s data structures, often corresponding to database tables.

Employee Model

Create a file named Employee.cs in the Models folder, and then copy and paste the following code. This class represents an employee and includes properties such as personal details (name, email), official IDs (PAN, ESI, etc.), job details (designation, department, joining date), bank information, and a navigation property to link to the payslips for that employee.

namespace SecurePayslipApp.Models
{
    public class Employee
    {
        public int EmployeeId { get; set; }

        // Basic Info
        public string EmpCode { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }

        // Official IDs
        public string PAN { get; set; }
        public string ESI { get; set; }
        public string PFNo { get; set; }
        public string UAN { get; set; }

        // Job Details
        public string Designation { get; set; }
        public string Department { get; set; }
        public DateTime DateOfJoining { get; set; }

        // Bank & Location
        public string BankName { get; set; }
        public string BankAccountNo { get; set; }
        public string Location { get; set; }

        // Navigation property
        public ICollection<Payslip> Payslips { get; set; }
    }
}
Payslip Model

Create a file named Payslip.cs in the Models folder, and then copy and paste the following code. This class represents a salary slip. It contains properties for salary components (basic, allowances), deductions, totals (earnings, deductions, net pay), attendance details, and an audit field (GeneratedOn). It also defines a foreign key relationship to the Employee model, indicating that each payslip is associated with one employee.

using System.ComponentModel.DataAnnotations.Schema;

namespace SecurePayslipApp.Models
{
    public class Payslip
    {
        public int PayslipId { get; set; }
        public int EmployeeId { get; set; }
        public Employee Employee { get; set; }

        // Which month (e.g., December-2020)
        public string Month { get; set; }

        // Salary Breakdown
        [Column(TypeName ="decimal(18,2)")]
        public decimal Basic { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal HouseRentAllowance { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal LtaThroughSalary { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal SpecialAllowance { get; set; }

        // Deductions
        [Column(TypeName = "decimal(18,2)")]
        public decimal PF { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal ProfessionTax { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal LabourWelfareFund { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal IncomeTax { get; set; }

        // Attendance data
        [Column(TypeName = "decimal(18,2)")]
        public double DaysPayable { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public double LOPDays { get; set; }

        // Totals
        [Column(TypeName = "decimal(18,2)")]
        public decimal TotalEarnings { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal TotalDeductions { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal NetPay { get; set; }
        public string NetPayInWords { get; set; }

        // Audit
        public DateTime GeneratedOn { get; set; }
    }
}
DbContext with Seed Data

First, create a folder named Data in the project root directory. Then, inside the Data folder, create a class file named ApplicationDbContext.cs and copy and paste the following code.

The ApplicationDbContext class inherits from DbContext and bridges your application and the database. It defines two DbSets, one for Employees and one for Payslips, allowing us to query and save instances of these models. In the OnModelCreating method, we seed initial data into the database for testing or demonstration purposes.

using Microsoft.EntityFrameworkCore;
using SecurePayslipApp.Models;

namespace SecurePayslipApp.Data
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
        { }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            // --- Seed Employees ---
            modelBuilder.Entity<Employee>().HasData(
                new Employee
                {
                    EmployeeId = 1,
                    EmpCode = "EMP001",
                    FirstName = "John",
                    LastName = "Doe",
                    ESI = "ESI12345",
                    PFNo = "PF0012345",
                    UAN = "UAN0012345",
                    Designation = "Software Engineer",
                    Department = "IT",
                    DateOfJoining = new DateTime(2021, 1, 15),
                    BankName = "HDFC Bank",
                    BankAccountNo = "5010022610123",
                    Location = "Mumbai",
                    PAN = "ABCDP1234E",
                    Email = "pranayakumar777@gmail.com"
                },
                new Employee
                {
                    EmployeeId = 2,
                    EmpCode = "EMP002",
                    FirstName = "Jane",
                    LastName = "Smith",
                    ESI = "ESI67890",
                    PFNo = "PF0098765",
                    UAN = "UAN0098765",
                    Designation = "Senior Developer",
                    Department = "IT",
                    DateOfJoining = new DateTime(2020, 5, 10),
                    BankName = "ICICI Bank",
                    BankAccountNo = "5010022610987",
                    Location = "Pune",
                    PAN = "EFGHP6789Q",
                    Email = "pranayakumar7@gmail.com"
                }
            );

            // --- Seed Payslips ---
            // For demonstration, we'll pick the last two months, e.g., December 2024 & January 2025
            // but you can adjust as you wish.
            modelBuilder.Entity<Payslip>().HasData(
                new Payslip
                {
                    PayslipId = 1,
                    EmployeeId = 1,
                    Month = "January-2025",
                    Basic = 30000,
                    HouseRentAllowance = 10000,
                    LtaThroughSalary = 2000,
                    SpecialAllowance = 5000,
                    // E + E + E + E
                    TotalEarnings = 30000 + 10000 + 2000 + 5000,

                    PF = 3600,
                    ProfessionTax = 200,
                    LabourWelfareFund = 12,
                    IncomeTax = 2500,
                    DaysPayable = 31,
                    LOPDays = 0,
                    // D + D + D + D
                    TotalDeductions = 3600 + 200 + 12 + 2500,

                    NetPay = 30000 + 10000 + 2000 + 5000 - (3600 + 200 + 12 + 2500),
                    NetPayInWords = "Indian rupees Thirty Seven Thousand Seven Hundred only",
                    GeneratedOn = DateTime.Today.AddDays(-30)
                },
                new Payslip
                {
                    PayslipId = 2,
                    EmployeeId = 1,
                    Month = "December-2024",
                    Basic = 31000,
                    HouseRentAllowance = 10200,
                    LtaThroughSalary = 2200,
                    SpecialAllowance = 5200,
                    TotalEarnings = 31000 + 10200 + 2200 + 5200,

                    PF = 3700,
                    ProfessionTax = 200,
                    LabourWelfareFund = 12,
                    IncomeTax = 2600,
                    DaysPayable = 28,
                    LOPDays = 0,
                    TotalDeductions = 3700 + 200 + 12 + 2600,

                    NetPay = 31000 + 10200 + 2200 + 5200 - (3700 + 200 + 12 + 2600),
                    NetPayInWords = "Indian rupees Thirty Eight Thousand Nine Hundred only",
                    GeneratedOn = DateTime.Today.AddDays(-3)
                },
                new Payslip
                {
                    PayslipId = 3,
                    EmployeeId = 2,
                    Month = "January-2025",
                    Basic = 40000,
                    HouseRentAllowance = 15000,
                    LtaThroughSalary = 3000,
                    SpecialAllowance = 6000,
                    TotalEarnings = 40000 + 15000 + 3000 + 6000,

                    PF = 4800,
                    ProfessionTax = 200,
                    LabourWelfareFund = 12,
                    IncomeTax = 3500,
                    DaysPayable = 31,
                    LOPDays = 0,
                    TotalDeductions = 4800 + 200 + 12 + 3500,

                    NetPay = 40000 + 15000 + 3000 + 6000 - (4800 + 200 + 12 + 3500),
                    NetPayInWords = "Indian rupees Fifty Thousand Four Hundred and Eighty Eight only",
                    GeneratedOn = DateTime.Today.AddDays(-30)
                },
                new Payslip
                {
                    PayslipId = 4,
                    EmployeeId = 2,
                    Month = "December-2024",
                    Basic = 42000,
                    HouseRentAllowance = 15800,
                    LtaThroughSalary = 3200,
                    SpecialAllowance = 6200,
                    TotalEarnings = 42000 + 15800 + 3200 + 6200,

                    PF = 5000,
                    ProfessionTax = 200,
                    LabourWelfareFund = 12,
                    IncomeTax = 3700,
                    DaysPayable = 28,
                    LOPDays = 0,
                    TotalDeductions = 5000 + 200 + 12 + 3700,

                    NetPay = 42000 + 15800 + 3200 + 6200 - (5000 + 200 + 12 + 3700),
                    NetPayInWords = "Indian rupees Fifty Three Thousand Ninety Only",
                    GeneratedOn = DateTime.Today.AddDays(-3)
                }
            );
        }

        public DbSet<Employee> Employees { get; set; }
        public DbSet<Payslip> Payslips { get; set; }
    }
}
Modifying AppSettings.json File:

This file holds various configuration settings used by the application:

  • Logging Settings: Configure the level of logging for the application.
  • PDFSettings: Contains settings for generating PDF documents, such as the owner password and the path to the company logo.
  • ConnectionStrings: Provides the connection string required to connect to the SQL Server database.
  • EmailSettings: This holds the SMTP configuration details (server, port, sender’s email, password, etc.) needed by the EmailService to send emails.

So, please modify the appsettings.json file as follows.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "PDFSettings": {
    "OwnerPassword": "SomeComplexOwnerPassword", // You can store this securely
    "LogoPath": "wwwroot/images/logo.png"
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=LAPTOP-6P5NK25R\\SQLSERVER2022DEV;Database=EmployeesDB;Trusted_Connection=True;TrustServerCertificate=True;"
  },
  "EmailSettings": {
    "SmtpServer": "smtp.gmail.com",
    "SmtpPort": "587",
    "SenderName": "[Sender Name]",
    "SenderEmail": "[Your Gmail Account]",
    "Password": "[Your Gmail App Password]"
  }
}
PDF Generation Service With iText7

Now, we will create a service class to generate PDF documents for payslips using the iText7 library. The following are the key features:

  • Configures encryption on the PDF using an owner password from configuration and a user password derived from the employee’s PAN.
  • Creates the PDF with various sections (header, employee details, earnings/deductions, disclaimer), ensuring the document is both informative and secure.

First, create a folder named Services in the project root directory. Then, inside the Services folder, create a class file named PDFGeneratorService.cs and copy and paste the following code.

using iText.IO.Font.Constants;           
using iText.IO.Image;                    
using iText.Kernel.Colors;               
using iText.Kernel.Font;                 
using iText.Kernel.Pdf;                  
using iText.Kernel.Pdf.Canvas.Draw;      
using iText.Layout;                      
using iText.Layout.Borders;             
using iText.Layout.Element;              
using iText.Layout.Properties;           
using SecurePayslipApp.Models;           

namespace SecurePayslipApp.Services
{
    public class PdfGeneratorService
    {
        // Field to hold the configuration settings.
        private readonly IConfiguration _configuration;

        // Constructor injecting IConfiguration to access app settings.
        public PdfGeneratorService(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        // Method to generate a PDF for a given Employee's Payslip.
        public byte[] GeneratePayslipPdf(Employee emp, Payslip slip)
        {
            try
            {
                // Create font instances for normal and bold text.
                PdfFont normalFont = PdfFontFactory.CreateFont(StandardFonts.HELVETICA);
                PdfFont boldFont = PdfFontFactory.CreateFont(StandardFonts.HELVETICA_BOLD);

                // Retrieve passwords from configuration; owner password is for full access,
                // while user password (using employee PAN) restricts permissions.
                var ownerPassword = _configuration["PDFSettings:OwnerPassword"] ?? "SomeComplexOwnerPassword";
                var userPassword = emp.PAN; // Using the employee's PAN as the PDF user password.

                // Create a MemoryStream to hold the generated PDF bytes.
                using (var ms = new MemoryStream())
                {
                    // Configure writer properties to enable PDF encryption.
                    var writerProperties = new WriterProperties()
                        .SetStandardEncryption(
                            System.Text.Encoding.UTF8.GetBytes(userPassword),   // User password bytes.
                            System.Text.Encoding.UTF8.GetBytes(ownerPassword),  // Owner password bytes.
                            EncryptionConstants.ALLOW_PRINTING,                 // Permissions: allow printing.
                            EncryptionConstants.ENCRYPTION_AES_128              // Use AES 128-bit encryption.
                        );

                    // Create a PdfWriter with the MemoryStream and encryption settings.
                    using (var writer = new PdfWriter(ms, writerProperties))

                    // Initialize a PdfDocument with the writer.
                    using (var pdf = new PdfDocument(writer))

                    // Create a Document which provides the high-level layout API.
                    using (var document = new Document(pdf))
                    {
                        // Set document margins.
                        document.SetMargins(20, 20, 20, 20);

                        // --------------------------------------------------------------------
                        // 1) BRANDED HEADER SECTION
                        // --------------------------------------------------------------------
                        // Define a brand color (Dark teal).
                        Color brandColor = new DeviceRgb(54, 117, 136);

                        // Define a table with two columns (70% for company details, 30% for logo).
                        float[] headerColWidths = { 70f, 30f };
                        Table headerTable = new Table(UnitValue.CreatePercentArray(headerColWidths))
                            .UseAllAvailableWidth()                  // Use the full available width.
                            .SetBackgroundColor(brandColor)          // Set background color for branding.
                            .SetMarginBottom(5);                     // Margin at the bottom.

                        // LEFT COLUMN: Company details.
                        Cell companyCell = new Cell()
                            .SetBorder(Border.NO_BORDER)             // No border for a clean look.
                            .SetPadding(10);                         // Padding inside the cell.
                        // Add company name in bold white text.
                        companyCell.Add(new Paragraph("DOT NET TUTORIALS PVT. LTD.")
                            .SetFont(boldFont)
                            .SetFontSize(12)
                            .SetFontColor(ColorConstants.WHITE));
                        // Add company address in normal white text.
                        companyCell.Add(new Paragraph("Alpine Villa, 2nd Floor, Bajarang Vihar,\nAndheri-Kurla Road, Andheri (E), Mumbai - 400059")
                            .SetFont(normalFont)
                            .SetFontSize(9)
                            .SetFontColor(ColorConstants.WHITE));
                        // Add contact details.
                        companyCell.Add(new Paragraph("Phone: +91 12 2345 7890 | Email: info@dotnetutorials.net")
                            .SetFont(normalFont)
                            .SetFontSize(9)
                            .SetFontColor(ColorConstants.WHITE));
                        // Add the cell to the header table.
                        headerTable.AddCell(companyCell);

                        // RIGHT COLUMN: Company logo.
                        // Retrieve the logo path from configuration.
                        string logoPath = _configuration["PDFSettings:LogoPath"] ?? "wwwroot/images/logo.png";
                        Cell logoCell = new Cell()
                            .SetBorder(Border.NO_BORDER)
                            .SetPadding(10)
                            .SetTextAlignment(TextAlignment.RIGHT)   // Text alignment to right.
                            .SetHorizontalAlignment(HorizontalAlignment.RIGHT); // Cell alignment to right.

                        // Check if the logo file exists before adding.
                        if (!string.IsNullOrEmpty(logoPath) && File.Exists(logoPath))
                        {
                            // Load the image data.
                            ImageData logoData = ImageDataFactory.Create(logoPath);
                            // Create an Image instance.
                            Image logo = new Image(logoData);
                            logo.ScaleAbsolute(80, 50);             // Set fixed dimensions for the logo.
                            logo.SetHorizontalAlignment(HorizontalAlignment.RIGHT);
                            logoCell.Add(logo);                      // Add the logo to the cell.
                        }
                        // Add the logo cell to the header table.
                        headerTable.AddCell(logoCell);

                        // Add the complete header table to the document.
                        document.Add(headerTable);

                        // --------------------------------------------------------------------
                        // 2) TITLE: "Payslip for the month of ...."
                        // --------------------------------------------------------------------
                        // Create a title paragraph using the payslip month.
                        Paragraph payslipTitle = new Paragraph($"Payslip for the month of {slip.Month}")
                            .SetFont(boldFont)
                            .SetFontSize(11)
                            .SetTextAlignment(TextAlignment.CENTER)
                            .SetMarginTop(10)
                            .SetMarginBottom(5);
                        // Add the title to the document.
                        document.Add(payslipTitle);

                        // Draw a line separator below the title.
                        LineSeparator line = new LineSeparator(new SolidLine(1));
                        document.Add(line.SetMarginBottom(10));

                        // --------------------------------------------------------------------
                        // 3) EMPLOYEE DETAILS SECTION
                        // --------------------------------------------------------------------
                        // Add a heading for the Employee Details section.
                        document.Add(CreateSectionHeading("Employee Details", boldFont));

                        // Define column widths for the employee details table.
                        float[] empDetailsWidths = { 2.5f, 3f, 2.5f, 3f };
                        Table empTable = new Table(UnitValue.CreatePointArray(empDetailsWidths))
                            .UseAllAvailableWidth()
                            .SetMarginBottom(10);

                        // Add employee information rows:
                        empTable.AddCell(CreateCellLabel("Emp Code", boldFont));
                        empTable.AddCell(CreateCellValue(emp.EmpCode, normalFont));
                        empTable.AddCell(CreateCellLabel("ESI No", boldFont));
                        empTable.AddCell(CreateCellValue(emp.ESI, normalFont));

                        empTable.AddCell(CreateCellLabel("Emp Name", boldFont));
                        empTable.AddCell(CreateCellValue($"{emp.FirstName} {emp.LastName}", normalFont));
                        empTable.AddCell(CreateCellLabel("PF No", boldFont));
                        empTable.AddCell(CreateCellValue(emp.PFNo, normalFont));

                        empTable.AddCell(CreateCellLabel("Designation", boldFont));
                        empTable.AddCell(CreateCellValue(emp.Designation, normalFont));
                        empTable.AddCell(CreateCellLabel("UAN", boldFont));
                        empTable.AddCell(CreateCellValue(emp.UAN, normalFont));

                        empTable.AddCell(CreateCellLabel("Location", boldFont));
                        empTable.AddCell(CreateCellValue(emp.Location, normalFont));
                        empTable.AddCell(CreateCellLabel("Bank Name", boldFont));
                        empTable.AddCell(CreateCellValue(emp.BankName, normalFont));

                        empTable.AddCell(CreateCellLabel("D.O.J", boldFont));
                        empTable.AddCell(CreateCellValue(emp.DateOfJoining.ToString("dd MMM yyyy"), normalFont));
                        empTable.AddCell(CreateCellLabel("Days Payable", boldFont));
                        empTable.AddCell(CreateCellValue(slip.DaysPayable.ToString(), normalFont));

                        empTable.AddCell(CreateCellLabel("PAN", boldFont));
                        empTable.AddCell(CreateCellValue(emp.PAN, normalFont));
                        empTable.AddCell(CreateCellLabel("LOP Days", boldFont));
                        empTable.AddCell(CreateCellValue(slip.LOPDays.ToString(), normalFont));

                        // Add the employee details table to the document.
                        document.Add(empTable);

                        // --------------------------------------------------------------------
                        // 4) EARNINGS & DEDUCTIONS SECTION
                        // --------------------------------------------------------------------
                        // Add a heading for Earnings & Deductions.
                        document.Add(CreateSectionHeading("Earnings & Deductions", boldFont));

                        // Define the column widths for the earnings and deductions table.
                        float[] earnDedWidths = { 3f, 2f, 3f, 2f };
                        Table earnDedTable = new Table(UnitValue.CreatePointArray(earnDedWidths))
                            .UseAllAvailableWidth();

                        // Create table header cells.
                        earnDedTable.AddHeaderCell(CreateHeaderCell("Earnings", boldFont));
                        earnDedTable.AddHeaderCell(CreateHeaderCell("Amount", boldFont));
                        earnDedTable.AddHeaderCell(CreateHeaderCell("Deductions", boldFont));
                        earnDedTable.AddHeaderCell(CreateHeaderCell("Amount", boldFont));

                        // Row for Basic salary and PF deduction.
                        earnDedTable.AddCell(CreateCellValue("Basic", normalFont));
                        earnDedTable.AddCell(CreateCellValue(slip.Basic.ToString("0.00"), normalFont));
                        earnDedTable.AddCell(CreateCellValue("PF", normalFont));
                        earnDedTable.AddCell(CreateCellValue(slip.PF.ToString("0.00"), normalFont));

                        // Row for House Rent Allowance and Profession Tax.
                        earnDedTable.AddCell(CreateCellValue("House Rent Allowance", normalFont));
                        earnDedTable.AddCell(CreateCellValue(slip.HouseRentAllowance.ToString("0.00"), normalFont));
                        earnDedTable.AddCell(CreateCellValue("Profession Tax", normalFont));
                        earnDedTable.AddCell(CreateCellValue(slip.ProfessionTax.ToString("0.00"), normalFont));

                        // Row for LTA and Labour Welfare Fund.
                        earnDedTable.AddCell(CreateCellValue("LTA Through Salary", normalFont));
                        earnDedTable.AddCell(CreateCellValue(slip.LtaThroughSalary.ToString("0.00"), normalFont));
                        earnDedTable.AddCell(CreateCellValue("Labour Welfare Fund", normalFont));
                        earnDedTable.AddCell(CreateCellValue(slip.LabourWelfareFund.ToString("0.00"), normalFont));

                        // Row for Special Allowance and Income Tax.
                        earnDedTable.AddCell(CreateCellValue("Special Allowance", normalFont));
                        earnDedTable.AddCell(CreateCellValue(slip.SpecialAllowance.ToString("0.00"), normalFont));
                        earnDedTable.AddCell(CreateCellValue("Income Tax", normalFont));
                        earnDedTable.AddCell(CreateCellValue(slip.IncomeTax.ToString("0.00"), normalFont));

                        // Totals row for Earnings and Deductions.
                        earnDedTable.AddCell(CreateBoldCell("Total Earnings", boldFont));
                        earnDedTable.AddCell(CreateBoldCell(slip.TotalEarnings.ToString("0.00"), boldFont));
                        earnDedTable.AddCell(CreateBoldCell("Total Deductions", boldFont));
                        earnDedTable.AddCell(CreateBoldCell(slip.TotalDeductions.ToString("0.00"), boldFont));

                        // --------------------------------------------------------------------
                        // Merged Cell: Display Net Salary and Salary In Words
                        // Spanning 2 rows and 4 columns to occupy full width.
                        // --------------------------------------------------------------------
                        Cell netSalaryInWordsCell = new Cell(2, 4)
                            .SetBorder(new SolidBorder(ColorConstants.BLACK, 0.5f))
                            .SetPadding(5)
                            .SetVerticalAlignment(VerticalAlignment.MIDDLE);
                        netSalaryInWordsCell.Add(new Paragraph($"Net Salary: {slip.NetPay.ToString("0.00")}")
                            .SetFont(boldFont)
                            .SetFontSize(9));
                        netSalaryInWordsCell.Add(new Paragraph($"In Words: {slip.NetPayInWords}")
                            .SetFont(normalFont)
                            .SetFontSize(9));
                        // Add the merged cell to the table.
                        earnDedTable.AddCell(netSalaryInWordsCell);

                        // Set a bottom margin for the table and add it to the document.
                        earnDedTable.SetMarginBottom(10);
                        document.Add(earnDedTable);

                        // --------------------------------------------------------------------
                        // 5) DISCLAIMER / FOOTER SECTION
                        // --------------------------------------------------------------------
                        // Add a disclaimer paragraph at the bottom.
                        Paragraph disclaimer = new Paragraph("* This is a computer-generated salary slip. Hence, no signature is required.")
                            .SetFont(normalFont)
                            .SetFontSize(8)
                            .SetTextAlignment(TextAlignment.CENTER);
                        document.Add(disclaimer);

                        // Close the document to finalize the PDF.
                        document.Close();
                    }

                    // Return the generated PDF as a byte array.
                    return ms.ToArray();
                }
            }
            catch (Exception ex)
            {
                // In case of error, wrap and rethrow the exception.
                throw new Exception("Error generating PDF payslip", ex);
            }
        }

        // --------------------------------------------------------------------
        // HELPER METHODS FOR LAYOUT
        // --------------------------------------------------------------------

        // Helper to create a section heading with specified style.
        private Paragraph CreateSectionHeading(string title, PdfFont headingFont)
        {
            return new Paragraph(title)
                .SetFont(headingFont)
                .SetFontSize(10)
                .SetFontColor(ColorConstants.BLACK)
                .SetBackgroundColor(new DeviceRgb(230, 230, 230))
                .SetPaddingLeft(5)
                .SetPaddingTop(3)
                .SetPaddingBottom(3)
                .SetMarginTop(10)
                .SetMarginBottom(5);
        }

        // Helper to create a cell with a label (bold text).
        private Cell CreateCellLabel(string text, PdfFont boldFont)
        {
            var p = new Paragraph(text)
                .SetFont(boldFont)
                .SetFontSize(9);
            return new Cell(1, 1)
                .Add(p)
                .SetBorder(new SolidBorder(ColorConstants.BLACK, 0.5f))
                .SetPadding(4)
                .SetVerticalAlignment(VerticalAlignment.MIDDLE);
        }

        // Helper to create a cell with a value (normal text).
        private Cell CreateCellValue(string text, PdfFont normalFont)
        {
            var p = new Paragraph(text)
                .SetFont(normalFont)
                .SetFontSize(9);
            return new Cell(1, 1)
                .Add(p)
                .SetBorder(new SolidBorder(ColorConstants.BLACK, 0.5f))
                .SetPadding(4)
                .SetVerticalAlignment(VerticalAlignment.MIDDLE);
        }

        // Helper to create a header cell with centered white text.
        private Cell CreateHeaderCell(string text, PdfFont boldFont)
        {
            var p = new Paragraph(text)
                .SetFont(boldFont)
                .SetFontSize(9)
                .SetFontColor(ColorConstants.WHITE);
            var headerColor = new DeviceRgb(45, 96, 111);
            return new Cell(1, 1)
                .Add(p)
                .SetBackgroundColor(headerColor)
                .SetBorder(new SolidBorder(ColorConstants.BLACK, 0.5f))
                .SetPadding(4)
                .SetTextAlignment(TextAlignment.CENTER)
                .SetVerticalAlignment(VerticalAlignment.MIDDLE);
        }

        // Helper to create a bold cell for totals.
        private Cell CreateBoldCell(string text, PdfFont boldFont)
        {
            var p = new Paragraph(text)
                .SetFont(boldFont)
                .SetFontSize(9);
            return new Cell(1, 1)
                .Add(p)
                .SetBorder(new SolidBorder(ColorConstants.BLACK, 0.5f))
                .SetPadding(4)
                .SetVerticalAlignment(VerticalAlignment.MIDDLE);
        }
    }
}
Creating the EmailService

Now, we will create an Email Service class to send emails with the generated PDF payslip attached. The following are the key features:

  • Reads SMTP and email settings from the configuration.
  • Constructs and sends an email asynchronously with the PDF attachment using the SMTP client.

So, create a class file named EmailService.cs within the Services folder and copy and paste the following code.

using System.Net.Mail;   
using System.Net;        

namespace SecurePayslipApp.Services
{
    public class EmailService
    {
        // Holds the configuration settings (e.g., from appsettings.json).
        private readonly IConfiguration _configuration; 

        // Constructor: Dependency injects IConfiguration to access email settings.
        public EmailService(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        // Asynchronously sends an email with a PDF attachment.
        public async Task<bool> SendEmailWithAttachmentAsync(string toEmail, string subject, string body, byte[] pdfAttachment, string attachmentName, bool isBodyHtml = false)
        {
            try
            {
                // Retrieve email settings from configuration file.
                string smtpServer = _configuration.GetValue<string>("EmailSettings:SmtpServer") ?? ""; // SMTP server address.
                int smtpPort = int.Parse(_configuration.GetValue<string>("EmailSettings:SmtpPort") ?? "587"); // SMTP server port.
                string senderName = _configuration.GetValue<string>("EmailSettings:SenderName") ?? "My Estore App"; // Name displayed as sender.
                string senderEmail = _configuration.GetValue<string>("EmailSettings:SenderEmail") ?? ""; // Sender's email address.
                string password = _configuration.GetValue<string>("EmailSettings:Password") ?? ""; // Password for the sender's email.

                // Create a new MailMessage to construct the email.
                using (MailMessage message = new MailMessage())
                {
                    // Set the sender's email and display name.
                    message.From = new MailAddress(senderEmail, senderName);

                    // Add the recipient's email address.
                    message.To.Add(new MailAddress(toEmail));

                    // Set the subject of the email.
                    message.Subject = subject;

                    // Set the body content of the email.
                    message.Body = body;

                    // Specify if the email body contains HTML.
                    message.IsBodyHtml = isBodyHtml;

                    // Create a new attachment from the provided PDF byte array.
                    // The attachmentName is the file name and "application/pdf" is the MIME type.
                    message.Attachments.Add(new Attachment(new MemoryStream(pdfAttachment), attachmentName, "application/pdf"));

                    // Create an SMTP client using the SMTP server and port.
                    using (var client = new SmtpClient(smtpServer, smtpPort))
                    {
                        // Set the client's credentials using the sender's email and password.
                        client.Credentials = new NetworkCredential(senderEmail, password);

                        // Enable SSL to secure the email transmission.
                        client.EnableSsl = true;

                        // Send the email asynchronously.
                        await client.SendMailAsync(message);
                    }
                }
                // Return true if email was sent successfully.
                return true;
            }
            catch (Exception ex)
            {
                // In case of any exception, you can log the exception here.
                // For now, return false to indicate the email was not sent.
                return false;
            }
        }
    }
}
PayslipController

Controllers handle HTTP requests, process user inputs, and coordinate responses by interacting with models, services, and views. Create an MVC Empty Controller named PayslipController within the Controllers folder, then copy and paste the following code. The PayslipController manages actions such as listing payslips, downloading PDF payslips, and sending emails for last month’s payslips.

using Microsoft.AspNetCore.Mvc;             
using Microsoft.EntityFrameworkCore;          
using SecurePayslipApp.Data;                 
using SecurePayslipApp.Services;             

namespace SecurePayslipApp.Controllers
{
    public class PayslipController : Controller
    {
        // Database context for accessing payslips and employee data.
        private readonly ApplicationDbContext _context;

        // Service for generating PDF payslips.
        private readonly PdfGeneratorService _pdfGenerator;

        // Service for sending emails with attachments.
        private readonly EmailService _emailService;

        // Constructor that accepts dependencies via dependency injection.
        public PayslipController(
            ApplicationDbContext context,
            PdfGeneratorService pdfGenerator,
            EmailService emailService)
        {
            _context = context;
            _pdfGenerator = pdfGenerator;
            _emailService = emailService;
        }

        // GET: Payslip/Index
        // Displays a list of recent payslips along with a button to send last month's payslips.
        [HttpGet]
        public async Task<IActionResult> Index()
        {
            // Get the current date and time.
            var now = DateTime.Now;

            // Determine the first day of the current month.
            var firstDayOfCurrentMonth = new DateTime(now.Year, now.Month, 1);

            // Calculate the date for last month and the month before last.
            var lastMonthDate = firstDayOfCurrentMonth.AddMonths(-1);
            var secondLastMonthDate = firstDayOfCurrentMonth.AddMonths(-2);

            // Format the dates as strings in "MMMM-yyyy" format (e.g., "February-2025").
            string lastMonthString = lastMonthDate.ToString("MMMM-yyyy");
            string secondLastMonthString = secondLastMonthDate.ToString("MMMM-yyyy");

            // Query the database for payslips matching the last two months.
            var recentPayslips = await _context.Payslips
                .AsNoTracking()                        // Optimize query since we only need read-only data.
                .Include(p => p.Employee)              // Include associated Employee data.
                .Where(p => p.Month == lastMonthString || p.Month == secondLastMonthString) // Filter by month.
                .OrderByDescending(p => p.GeneratedOn) // Order by the date the payslip was generated.
                .ToListAsync();

            // Return the view along with the list of recent payslips.
            return View(recentPayslips);
        }

        // POST: Payslip/SendLastMonthPayslips
        // Sends out PDF payslips for the last month via email to all employees who have an entry in the database.
        [HttpPost]
        public async Task<IActionResult> SendLastMonthPayslips()
        {
            try
            {
                // Determine the last month's date by subtracting one month from the first day of the current month.
                var lastMonthDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddMonths(-1);

                // Format last month's date as a string.
                string lastMonthString = lastMonthDate.ToString("MMMM-yyyy");

                // Fetch all payslips from the database that match the last month.
                var payslips = await _context.Payslips
                    .AsNoTracking()                     // Use no-tracking for read-only operations.
                    .Include(p => p.Employee)             // Include the related Employee data.
                    .Where(p => p.Month == lastMonthString) // Filter by the last month's string.
                    .ToListAsync();

                // If no payslips are found, set a message and redirect back to the index.
                if (payslips.Count == 0)
                {
                    TempData["Message"] = $"No payslips found for {lastMonthString}.";
                    return RedirectToAction(nameof(Index));
                }

                // Loop through each payslip to generate its PDF and send it via email.
                foreach (var slip in payslips)
                {
                    // Generate the PDF as a byte array using the PdfGeneratorService.
                    var pdfBytes = _pdfGenerator.GeneratePayslipPdf(slip.Employee, slip);

                    // Construct a professional HTML email body with employee-specific information.
                    string htmlBody = $@"
                        <html>
                        <body style='font-family: Arial; font-size: 14px;'>
                            <p>Dear {slip.Employee.FirstName} {slip.Employee.LastName},</p>
                            <p>Your salary slip for <strong>{slip.Month}</strong> has been processed and is attached to this email.</p>
                            <p>Please use your <strong>PAN</strong> (i.e. {slip.Employee.PAN}) to open the PDF file. 
                               Make sure you keep this information confidential.</p>
                            <p>If you face any issues opening the attachment, please reach out to the HR department.</p>
                            <p>Regards,<br/>HR Team</p>
                        </body>
                        </html>
                    ";

                    // Set the email subject.
                    string subject = $"Payslip for {slip.Month}";
                    // Send the email with the PDF attachment using the EmailService.
                    bool emailSent = await _emailService.SendEmailWithAttachmentAsync(
                        slip.Employee.Email,                         // Recipient's email address.
                        subject,                                     // Email subject.
                        htmlBody,                                    // Email body (HTML content).
                        pdfBytes,                                    // PDF attachment as a byte array.
                        $"Payslip_{slip.Employee.EmpCode}_{slip.Month}.pdf", // File name for the attachment.
                        isBodyHtml: true                             // Specify that the body is in HTML format.
                    );
                }

                // Set a success message using TempData and redirect back to the index.
                TempData["Message"] = $"Payslips for {lastMonthString} have been emailed successfully.";
                return RedirectToAction(nameof(Index));
            }
            catch (Exception ex)
            {
                // In case of an error, set an error message in TempData and redirect to the index.
                TempData["Error"] = $"Error occurred: {ex.Message}";
                return RedirectToAction(nameof(Index));
            }
        }

        // GET: Payslip/DownloadPayslip?payslipId=123
        // Allows users or admins to download a specific payslip PDF by its ID.
        [HttpGet]
        public async Task<IActionResult> DownloadPayslip(int payslipId)
        {
            try
            {
                // Retrieve the specified payslip along with its associated employee data.
                var payslip = await _context.Payslips
                    .AsNoTracking()                      // Use no-tracking since we're only reading.
                    .Include(p => p.Employee)              // Include related Employee data.
                    .FirstOrDefaultAsync(p => p.PayslipId == payslipId); // Filter by payslip ID.

                // If the payslip does not exist, return a 404 Not Found response.
                if (payslip == null)
                    return NotFound("Payslip not found.");

                // Generate the PDF for the retrieved payslip.
                var pdfBytes = _pdfGenerator.GeneratePayslipPdf(payslip.Employee, payslip);

                // Create a filename for the downloaded PDF.
                string fileName = $"Payslip_{payslip.Employee.EmpCode}_{payslip.Month}.pdf";
                // Return the PDF file as a downloadable file with the MIME type "application/pdf".
                return File(pdfBytes, "application/pdf", fileName);
            }
            catch (Exception ex)
            {
                // In case of an error, return a 500 Internal Server Error with the error message.
                return StatusCode(500, "Internal server error: " + ex.Message);
            }
        }
    }
}
Creating the Index View:

Views generate the HTML output seen by the user. They display data provided by the controller. So, create a view named Index.cshtml within the Views/Payslip folder, and then copy and paste the following code. The Index view displays a list of recent payslips, allows downloading PDF files, and provides an interface to trigger sending payslips via email.

@model IEnumerable<SecurePayslipApp.Models.Payslip>

@{
    ViewData["Title"] = "Payslip Management";
}

<div class="container mt-5">
    <div class="row">
        <div class="col-12">
            <!-- Card Container -->
            <div class="card shadow-sm">
                <div class="card-body">
                    <h2 class="card-title mb-4">Payslip Management</h2>

                    <!-- TempData messages -->
                    @if (TempData["Message"] != null)
                    {
                        <div class="alert alert-success alert-dismissible fade show" role="alert">
                            @TempData["Message"]
                            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                    }
                    @if (TempData["Error"] != null)
                    {
                        <div class="alert alert-danger alert-dismissible fade show" role="alert">
                            @TempData["Error"]
                            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                    }

                    <!-- Action Section -->
                    <div class="mb-4">
                        <form method="post" asp-action="SendLastMonthPayslips">
                            <button type="submit" class="btn btn-primary btn-lg">
                                Send Last Month's Payslips to All
                            </button>
                        </form>
                        <small class="form-text text-muted mt-2">
                            Clicking the above button will send out PDF payslips to all employees who have a payslip record for last month.
                        </small>
                        <div class="mt-2">
                            <strong>Note:</strong> The PDF attachments are password-protected. The Employee's <strong>PAN</strong> is used as the PDF password.
                            Please advise all recipients to use their PAN to open the PDF.
                        </div>
                    </div>

                    <!-- Recent Payslips Table -->
                    <h4 class="mb-3">Recent Payslips</h4>
                    <div class="table-responsive">
                        <table class="table table-bordered table-striped">
                            <thead class="thead-dark">
                                <tr>
                                    <th>Employee Code</th>
                                    <th>Employee Name</th>
                                    <th>Month</th>
                                    <th>Net Pay</th>
                                    <th>Download</th>
                                </tr>
                            </thead>
                            <tbody>
                                @foreach (var payslip in Model)
                                {
                                    <tr>
                                        <td>@payslip.Employee.EmpCode</td>
                                        <td>@($"{payslip.Employee.FirstName} {payslip.Employee.LastName}")</td>
                                        <td>@payslip.Month</td>
                                        <td>@payslip.NetPay.ToString("C")</td>
                                        <td>
                                            <a asp-action="DownloadPayslip" asp-route-payslipId="@payslip.PayslipId" class="btn btn-success btn-sm">
                                                Download PDF
                                            </a>
                                        </td>
                                    </tr>
                                }
                            </tbody>
                        </table>
                    </div>

                    <!-- Instructions Section -->
                    <div class="alert alert-info mt-4">
                        <h5 class="alert-heading">Instructions to Open the PDF File</h5>
                        <ol class="mb-0">
                            <li>Download your payslip PDF by clicking the download button above.</li>
                            <li>Use your <strong>PAN</strong> (as recorded in your profile) as the password to open the file.</li>
                            <li>Contact HR if you face any issues.</li>
                        </ol>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
Modify the Program Class as follows:

The Program class serves as the entry point of the application. It:

  • Configures services (MVC controllers, Entity Framework DbContext, and custom services like PDF and Email services).
  • Sets up middleware for HTTPS redirection, static files, routing, and authorization.
  • Defines the default controller route and finally runs the application.

So, please modify the Program class as follows:

using Microsoft.EntityFrameworkCore;
using SecurePayslipApp.Data;
using SecurePayslipApp.Services;

namespace SecurePayslipApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddControllersWithViews();

            // Configure SQL Server DbContext
            builder.Services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))
            );

            // Register custom services
            builder.Services.AddScoped<PdfGeneratorService>();
            builder.Services.AddScoped<EmailService>();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.MapControllerRoute(
                name: "default",
                pattern: "{controller=Payslip}/{action=Index}/{id?}");

            app.Run();
        }
    }
}
Database Migrations

Open the Package Manager Console in Visual Studio, and then execute the following commands:

  • Add-Migration Mig1
  • Update-Database

This should be the EmployeesDB with the required Employees and Payslips tables, as shown in the image below.

How to generate a Password-protected PDF in an ASP.NET Core MVC application with examples

Now, run the application and test the functionalities. It should work as expected.

User and Owner Password in WriterProperties

In the context of creating or manipulating PDFs in ASP.NET Core, mainly when using libraries like iText or similar, the terms “User Password” and “Owner Password” refer to two different levels of security that can be applied to a PDF document. These are set through WriterProperties or similar configurations. The following code we use in our example:

User and Owner Password in WriterProperties

User Password:
  • The User Password, also known as the document open password, is what users need to enter to open and view the PDF document.
  • If a PDF is secured with a User Password, it cannot be opened and read by anyone who does not have the password.
  • This level of password protection is used when the document creator wants to restrict access to specific individuals.
Owner Password:
  • The Owner Password, sometimes called the permissions password, allows the setting of various permissions and restrictions on what can be done with the PDF once it’s opened.
  • This might include restrictions on printing, copying text or images, editing, commenting, filling in forms, etc.
  • Users can open and view the document without needing the Owner Password, but they will be restricted from performing specific actions unless they have this password.
  • The Owner Password is used when the document creator wants to allow people to view the document but restrict certain functionalities.
How to Restrict Printing and Copying in Password-Protected PDFs?

To create a Password-Protected PDF and restrict printing and copying using iText, we need to set the appropriate permissions when we apply the password protection. iText allows us to set various permissions, including restricting printing, modifying, and copying of the document. So, please modify the WriterProperties object within the GeneratePayslipPdf method as follows.

How to Restrict Printing and Copying in Password-Protected PDFs?

By setting the permission bitmask to 0, we do not include flags such as EncryptionConstants.ALLOW_PRINTING or EncryptionConstants.ALLOW_COPY, so these actions are restricted. This restriction is for the user’s password, not for the owner’s password.

Opening the PDF with User Password:

With the above settings, if you open the PDF using the user password, you will see that both Print and Copy options are disabled, as shown in the image below.

Opening the PDF with User Password

Opening the PDF with the Owner Password:

However, the owner’s password is not restricted. If you open the document using the owner’s password, you should be able to copy and print the PDF document, as shown in the image below.

Opening the PDF with the Owner Password

How Do We Restrict Printing But Allow Copying in Password-Protected PDFs?

To restrict printing while still allowing copying, you need to grant only the copy permission when setting up PDF encryption. In iText7, this is achieved by supplying only the EncryptionConstants.ALLOW_COPY flag (and not including the printing flag). So, please modify the WriterProperties object within the GeneratePayslipPdf method as follows. By setting the permissions flag to allow copying, we explicitly permit text and graphics to be copied while not granting any permission to print.

How Do We Restrict Printing But Allow Copying in Password-Protected PDFs?

How Do We Apply Multiple Permissions to Password-Protected PDFs?

We can apply multiple permissions by combining flags using a bitwise OR (|) operation. Each permission is represented by a constant in EncryptionConstants. By using bitwise OR, you create a composite permission value that enables all the specified actions. For example, if we want to allow copying and screen reading (but restrict printing), we could combine EncryptionConstants.ALLOW_COPY and EncryptionConstants.ALLOW_ALLOW_PRINTING like this:

How Do We Apply Multiple Permissions to Password-Protected PDFs?

How Do We Exclude the Owner Password in Password Protected PDF?

If you want to create a Password-Protected PDF without an Owner Password, you need to pass null for the owner password parameter in the SetStandardEncryption method. This will apply encryption and restrictions using only the User Password, meaning anyone with the User Password can open the document, but there will be no separate owner password to bypass the restrictions. Now, the PDF is password-protected for viewing (since a user password is set), but there is no separate owner password defined.

How Do We Exclude the Owner Password in Password Protected PDF?

Generating password-protected PDFs is a wise choice in various scenarios, especially when dealing with sensitive or confidential information. 

In the next article, I will discuss HTML to PDF Conversion in ASP.NET Core MVC Applications with examples. In this article, I explain how to Generate Password-Protected PDFs in ASP.NET Core MVC Applications with Examples. I hope you enjoy this article on how to generate Password-protected PDFs in ASP.NET Core MVC.

Leave a Reply

Your email address will not be published. Required fields are marked *