惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
博客园 - 司徒正美
TaoSecurity Blog
TaoSecurity Blog
Martin Fowler
Martin Fowler
T
Threat Research - Cisco Blogs
Blog — PlanetScale
Blog — PlanetScale
S
Secure Thoughts
博客园 - 三生石上(FineUI控件)
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
K
Kaspersky official blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
H
Help Net Security
博客园 - 叶小钗
爱范儿
爱范儿
GbyAI
GbyAI
I
Intezer
M
MIT News - Artificial intelligence
Latest news
Latest news
Schneier on Security
Schneier on Security
T
Tor Project blog
Simon Willison's Weblog
Simon Willison's Weblog
I
InfoQ
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
C
CXSECURITY Database RSS Feed - CXSecurity.com
罗磊的独立博客
N
News and Events Feed by Topic
T
The Blog of Author Tim Ferriss
V2EX - 技术
V2EX - 技术
B
Blog
T
Tailwind CSS Blog
N
Netflix TechBlog - Medium
Security Latest
Security Latest
V
V2EX
F
Fortinet All Blogs
Forbes - Security
Forbes - Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
The Hacker News
The Hacker News
Scott Helme
Scott Helme
P
Privacy International News Feed
P
Palo Alto Networks Blog
H
Heimdal Security Blog
C
Cisco Blogs
T
The Exploit Database - CXSecurity.com
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
G
Google Developers Blog
W
WeLiveSecurity
L
LINUX DO - 最新话题

Inside Nutrient

A guide to the invisible work behind documents Introducing Nutrient Documents for Salesforce: Native document generation and signing Document AI vs. traditional OCR: Choosing between OCR, AI, and hybrid pipelines PDF SDK compliance and security evaluation checklist for enterprise teams (2026) Invariant Corp replaces paper processes with Nutrient Workflow and scales without limits What is process mapping? A complete guide Nutrient vs. Conga Composer for Salesforce document generation (2026) Document routing: How to automate document distribution The CTO’s AI playbook: Why accountability architecture beats orchestration Compliance workflow automation: Why built-in compliance is table stakes Workflow diagrams: Examples, symbols, and how to build one that actually runs Digital forms: Replace paper forms with automated workflows Approval workflow software: How to automate approvals Why document-centric automation is different The CEO’s AI playbook: Why decision architecture beats model selection Nutrient SDK product updates for Q1 2026 PDF redaction verification: How to prove sensitive data is permanently removed What is a VPAT? The complete guide to accessibility conformance reports What is PDF/UA? The accessible PDF standard explained Salesforce eSignatures: Generate, sign, and track documents in one flow Online document viewer: Options, tradeoffs, and how to embed one Document viewer for web apps: React, Vue, Angular (2026) Best document viewers in 2026: A buyer’s guide How to edit a PDF in Python: Add text, images, and annotations Nutrient advances Workflow platform with agentic AI for enterprise-grade speed and consistency in document-heavy operations How to create a Salesforce quote template from opportunity data The business case for accessibility: Five ways it drives enterprise value Python PDF library comparison (2026): 7 libraries for developers Why your AI agent hallucinates PDF table data PDF.js limitations: When to upgrade to a commercial PDF SDK How Subject scaled 5× with Nutrient’s PDF SDK without rebuilding its document layer I replaced our sales training with an AI coach that runs in Slack — here’s what broke Redirecting to: https://securitybuzz.com/cybersecurity-news/why-enterprise-permissions-are-ais-most-dangerous-inheritance/ Nutrient .NET SDK vs. iText Core: Complete comparison for .NET developers DocuVieware: Support’s most frequently asked setup questions Introducing Nutrient Workflow How to convert PDF to Word in C# (.NET) When email and spreadsheets stop working: Work order approval workflows for field teams on the move Compliance with confidence: Why document-centric automation is the foundation of your mission Nutrient expands AI Assistant, automating multistep document workflows inside any application What is document generation? A developer’s guide to PDF generation Document Converter data flow and how real-time watermarks skip the queue PDF/UA compliance guide: Requirements, standards, and best practices Computers still can’t understand you How Athena Intelligence built AI agents for regulated enterprises with Nutrient’s document infrastructure How to convert HTML to PDF (2026): 4 methods from browser print to SDK How to build a document extraction pipeline with Nutrient Vision API OCR vs. intelligent document processing: Choosing the right document extraction engine Beyond OCR: How document intelligence eliminates manual processing in regulated industries Nutrient vs. IronPDF: Complete comparison for .NET developers Nutrient vs. Aspose.PDF: Complete comparison for .NET developers Redirecting to: https://fortune.com/2026/02/19/openclaw-who-is-peter-steinberger-openai-sam-altman-anthropic-moltbook/ Lufthansa Systems uses Nutrient to deliver reliable, scalable PDF rendering for pilots worldwide Nutrient vs. Syncfusion: Complete comparison for .NET developers React’s useTransition: The hook you’re probably using wrong First City Monument Bank streamlines banking processes with Nutrient Workflow Redirecting to: https://www.sdcexec.com/warehousing/automation/article/22957364/nutrient-workflow-automation-the-missing-link-in-supply-chain-efficiency The complete guide to digital signatures: PAdES, CAdES, and XAdES explained Nutrient Python SDK: Production-grade document processing for Python Introducing agentic document editing for web applications with AI Assistant Nutrient vs. QuestPDF: Complete comparison for .NET developers How we fixed the GdPicture license expiration (and what to do if you’re affected) Red team security testing with agentic AI The future of healthcare document automation Best healthcare workflow software compared Nutrient SDK product updates for Q4 2025 How Harvey scaled legal document workflows 50 percent MoM without rebuilding infrastructure HIPAA-compliant document management in hospitals How we optimized rendering performance while handling thousands of annotations in React — Part 2 Automated PII removal with Nutrient API Redirecting to: https://www.devopsdigest.com/2026-low-code-no-code-predictions Redirecting to: https://www.kmworld.com/Articles/Editorial/ViewPoints/Leaders-predict-AI-to-continue-permeating-all-aspects-of-KM-in-2026-172594.aspx What are deep agents and how do they solve complex problems? Whipping up document magic: Your easy-bake recipe for Vue and Nutrient Web SDK 🧁 What I’ve learned about product iteration planning while building SDKs Passwordless document signing: Three-layer security guide New zip folder functionality streamlines file management in Document Automation Server The keyboard shortcuts playbook: Taking control of keyboard events in Nutrient Web SDK From experienced engineer to AI beginner: My unexpected journey AI-assisted manual testing: Handling Safari’s PDF rendering and UI quirks How to keep a 20-year-old SDK up to date How we optimized rendering performance while handling thousands of annotations in React — Part 1 Nutrient announces new executive hires to accelerate next phase of growth High performance UI using web workers Automate document conversion at scale with Python and Nutrient DCS From curiosity to PLG (and AI): My journey to understanding product-led growth Prost to progress: One year as Nutrient Pigeon usage at Nutrient: Bridging native SDKs to Flutter Modernizing CI build servers: How to migrate from Chef to Ansible Unix man pages: AI-friendly documentation since 1971 Consistent hashing for even load distribution Best AI redaction APIs: Complete comparison guide for 2025 Why AI document redaction matters for modern security From coding to coordinating: How AI transformed my workflow What is intelligent document processing (IDP)? A complete guide Enterprise PDF SDKs: Best PSPDFKit (now Nutrient) alternatives Nutrient SDK product updates for Q3 2025 GdPicture support best practices Redacting sensitive data with Nutrient AI redaction API How AI is transforming the customer experience at Nutrient: From instant answers to intelligent support
How to add digital signatures to PDFs using Angular
Hulya Masharipov · 2023-11-14 · via Inside Nutrient

Table of contents

    How to add digital signatures to PDFs using Angular

    In this blog post, you’ll learn how to sign PDF documents with Angular and Nutrient Web SDK, using digital signatures to make sure your documents stay safe and reliable, especially when they’re particularly important — like legal contracts and official records.

    Nutrient Angular digital signature library

    Nutrient offers a range of essential capabilities for PDF signing. It streamlines the process of creating and validating electronic signatures, which encompass a range of signature types, such as hand-drawn, scanned, and typed signatures. While it’s important to note that electronic signature are, in essence, distinct from traditional digital signatures, their visual representations can serve as the appearance for a digital signature. An example illustrating this concept can be found in our demo.

    The ability to store signatures locally or remotely enhances convenience, while automated workflows can be triggered based on signature actions. Nutrient allows for user interface (UI) customization, ensuring a seamless user experience, and it supports client-side signing, eliminating the need for a dedicated server. Additionally, it can be extended to include forms, annotations, and other PDF-related functionalities, making it a versatile tool for various document tasks.

    Signature support

    Nutrient offers two types of signatures: electronic signatures and digital signatures.

    1. Electronic signatures allow users to create signatures with ink drawings, bitmap images, or text. Our Electronic Signatures component provides a user-friendly interface, supporting draw, image, and type signature modes, and it enables signature storage for reuse.
    2. Digital signatures, on the other hand, are signed with certificates, offering reliable proof of a document’s origin and protection against unauthorized changes. While distinct, these signature types can be used together for a comprehensive signing solution.

    Nutrient’s Angular PDF library

    We offer a commercial Angular PDF viewer library that can easily be integrated into your web application. It comes with 30+ features that let you view, annotate, edit, and sign documents directly in your browser. Out of the box, it has a polished and flexible UI that you can extend or simplify based on your unique use case.

    • A prebuilt UI — Save time with a well-documented list of APIs when customizing the UI to meet your exact requirements.
    • Annotation tools — Draw, circle, highlight, comment, and add notes to documents with 15+ prebuilt annotation tools.
    • Multiple file types — Support client-side viewing of PDFs, MS Office documents, and image files.
    • 30+ features — Easily add features like PDF editing, digital signatures, form filling, real-time document collaboration, and more.
    • Dedicated support — Deploy faster by working 1-on-1 with our developers.

    Requirements to get started

    To get started, you’ll need:

    When you install Node.js, npm is installed by default.

    Setup

    Go to your terminal and install the Angular CLI(opens in a new tab). This will help you get up and running quickly with Angular:

    npm install -g @angular/cli

    Now, check the version of Angular:

    Creating a new Angular project

    Now you’ll see how to integrate Nutrient into your Angular project.

    First, create a new Angular project for Nutrient integration:

    ng new pspdfkit-web-example-angular

    This will ask some configuration questions. Choose No for routing and CSS for the stylesheet.

    Now, change your directory to this project:

    cd pspdfkit-web-example-angular

    Adding Nutrient

    Install pspdfkit as a dependency with npm or yarn:

    Now, add the following to your angular.json file. Angular will copy the Nutrient library assets to the assets directory before running your app:

    "assets": [

    "src/favicon.ico",

    "src/assets",

    {

    "glob": "**/*",

    "input": "./node_modules/pspdfkit/dist/pspdfkit-lib/",

    "output": "./assets/pspdfkit-lib/"

    }

    ]

    Displaying the PDF

    In newer Angular versions, the assets directory may not be created automatically when setting up a new project. This change reflects updates in Angular’s project structure. If your project is missing the assets folder, you can easily create it under the src directory by running:

    After creating the folder, add your PDF document to the src/assets directory. Feel free to use our demo document as a sample.

    1. Replace the contents of app.component.html with:

    <div class="app">

    <div class="toolbar">

    <img class="logo" src="/favicon.ico" height="32" />

    PSPDFKit Angular Application

    </div>

    <!-- We'll mount the PSPDFKit UI to this element. -->

    <div class="pspdfkit-container"></div>

    </div>

    1. Replace the contents of app.component.ts with:

    import { Component } from "@angular/core";

    import PSPDFKit from "pspdfkit";

    @Component({

    selector: "app-root",

    templateUrl: "./app.component.html",

    styleUrls: ["app.component.css"],

    standalone: true,

    })

    export class AppComponent {

    title = "PSPDFKit for Web Angular Example";

    ngAfterViewInit(): void {

    PSPDFKit.load({

    // Use the assets directory URL as a base URL. Nutrient will download its library assets from here.

    baseUrl: location.protocol + "//" + location.host + "/assets/",

    document: "/assets/example.pdf",

    container: ".pspdfkit-container",

    licenseKey: "YOUR_LICENSE_KEY_GOES_HERE", // Optional license key.

    }).then((instance) => {

    // For the sake of this demo, store the Nutrient Web SDK instance

    // on the global object so that you can open the dev tools and

    // play with the Nutrient API.

    (<any>window).instance = instance;

    });

    }

    }

    The license key is optional; however, you may see a watermark on your PDF files without a key. To get a key, contact Sales.

    If you try to run your project, you may get an error stating the mounting container has no height. To fix this issue, add the following styles to the src/app/app.component.css file:

    :host {

    height: 100%;

    }

    .app {

    position: fixed;

    width: 100%;

    height: 100%;

    top: 0;

    right: 0;

    bottom: 0;

    left: 0;

    }

    .toolbar {

    position: relative;

    display: flex;

    align-items: center;

    height: 64px;

    width: 100%;

    padding: 0 24px;

    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);

    font-family: sans-serif;

    font-size: 20px;

    font-weight: 500;

    color: rgba(0, 0, 0, 0.8);

    }

    .logo {

    margin-right: 20px;

    }

    .pspdfkit-container {

    height: calc(100% - 64px);

    }

    You can access the project on GitHub(opens in a new tab).

    1. Start the app and open it in your default browser:

    Adding a digital signature to a PDF using Nutrient

    Nutrient requires an X.509 certificate(opens in a new tab) and a private key pair for adding a digital signature to a PDF document. The steps below outline how to do this.

    Step 1 — Generating a self-signed certificate and private key

    You can generate a self-signed certificate and private key using OpenSSL(opens in a new tab), which is a widely used tool for working with certificates and keys.

    1. Open your terminal in the project directory.
    2. Run the following OpenSSL command to generate a self-signed certificate and private key:

    openssl req -x509 -sha256 -nodes -newkey rsa:2048 -keyout private-key.pem -out cert.pem

    • -x509 — Tells OpenSSL to create a self-signed certificate.
    • -sha256 — Specifies the hash function to use for the certificate.
    • -nodes — Prevents encryption of the private key. You can remove this option for production keys if encryption is desired.
    • -newkey rsa:2048 — Generates a new RSA private key with a key size of 2,048 bits.
    • -keyout private-key.pem — Specifies the name of the private key file.
    • -out cert.pem — Specifies the name of the certificate file.

    Follow the prompts to provide information for the certificate, such as the Common Name (CN), organization, and location. These details will be embedded in the certificate.

    Self-signed certificates should only be used for development and testing purposes. In production environments, it’s crucial to obtain a certificate from a trusted Certificate Authority (CA) to ensure the security and authenticity of your application.

    Step 2 — Verifying your certificate

    After generating the certificate and private key, you can verify if the certificate is correctly PEM-encoded using the following command:

    openssl pkcs7 -noout -text -print_certs -in cert.pem

    This command will display certificate details and shouldn’t produce any errors. It confirms that “cert.pem” is a PEM-encoded X.509 certificate.

    Alternatively, if you want to verify DER-encoded certificates, you can use the following command:

    openssl pkcs7 -inform der -noout -text -print_certs -in cert.pem

    This command checks if “cert.pem” is a DER-encoded PKCS#7 certificate.

    With these steps, you’ll have generated the required X.509 certificate and private key pair for adding digital signatures to your PDF documents. Be certain to store these files securely, as they’re essential for signing documents.

    For more information on adding a digital signature to a PDF using Nutrient, refer to our digital signatures guide.

    Signing a PDF document using Nutrient

    To add a digital signature to your PDF document using Nutrient, follow the steps outlined below.

    Step 1 — Installing the Forge library

    1. Install the Forge library using npm. Open your terminal, navigate to the project directory, and run the following command:

    npm install node-forge

    npm i --save-dev @types/node-forge

    1. Import the Forge library in your app.component.ts file:

    import * as forge from 'node-forge';

    Step 2 — Generating the PKCS#7 signature

    Nutrient utilizes the cryptographic Distinguished Encoding Rules (DER) PKCS#7(opens in a new tab) format for digital signatures. You’ll need to create a valid PKCS#7 signature containing your certificate and other relevant information.

    Define a function, generatePKCS7, to generate the digital signature for your PDF. This function will perform the necessary cryptographic operations:

    function generatePKCS7({

    fileContents,

    }: {

    fileContents: ArrayBuffer | null,

    }): Promise<ArrayBuffer> {

    // Fetch the certificate and private key.

    const certificatePromise = fetch('cert.pem').then((response) =>

    response.text(),

    );

    const privateKeyPromise = fetch('private-key.pem').then((response) =>

    response.text(),

    );

    return new Promise((resolve, reject) => {

    Promise.all([certificatePromise, privateKeyPromise])

    .then(([certificatePem, privateKeyPem]) => {

    // Parse the certificate and private key using Forge.js.

    const certificate = forge.pki.certificateFromPem(

    certificatePem,

    );

    const privateKey = forge.pki.privateKeyFromPem(

    privateKeyPem,

    );

    // Create a PKCS7 signature.

    const p7 = forge.pkcs7.createSignedData();

    if (!fileContents) {

    throw new Error('No file contents provided.');

    }

    const buffer = forge.util.createBuffer(fileContents);

    p7.content = buffer.getBytes();

    p7.addCertificate(certificate);

    // Add the signer information.

    p7.addSigner({

    key: privateKey,

    certificate: certificate,

    digestAlgorithm: forge.pki.oids['sha256'],

    authenticatedAttributes: [

    {

    type: forge.pki.oids['contentType'],

    value: forge.pki.oids['data'],

    },

    {

    type: forge.pki.oids['messageDigest'],

    },

    {

    type: forge.pki.oids['signingTime'],

    value: new Date().toISOString(),

    },

    ],

    });

    // Sign the data.

    p7.sign({ detached: true });

    // Convert the result to an `ArrayBuffer`.

    const result = stringToArrayBuffer(

    forge.asn1.toDer(p7.toAsn1()).getBytes(),

    );

    resolve(result);

    })

    .catch(reject);

    });

    }

    This function fetches your certificate and private key, and it then uses Forge to create a PKCS#7 signed data structure.

    In professional settings, it’s essential to prioritize the security of your private key. While this example simplifies the process by fetching the private key from a file, in practice, you should consider more robust security measures. This includes storing the private key in a secure hardware device and potentially encrypting it. Also, please be aware that the .pem file generated with OpenSSL in this example isn’t encrypted, and in production, you should ensure the private key’s protection accordingly.

    Step 3 — Converting a string to an array buffer

    You’ll need a utility function, stringToArrayBuffer, to convert a binary string into an ArrayBuffer:

    function stringToArrayBuffer(binaryString: string): ArrayBuffer {

    const buffer = new ArrayBuffer(binaryString.length);

    let bufferView = new Uint8Array(buffer);

    for (let i = 0, len = binaryString.length; i < len; i++) {

    bufferView[i] = binaryString.charCodeAt(i);

    }

    return buffer;

    }

    Step 4 — Initializing Nutrient and signing the document

    Now, you can initialize Nutrient and invoke the PSPDFKit.Instance#signDocument method. This method takes two arguments.

    • Argument 1 — You can use this argument to fine-tune the signing process by providing essential data, such as certificates and private keys. If you don’t have specific signing requirements, you can pass null.
    • Argument 2 — In the second argument, supply fileContents. This parameter is used as a callback object containing an ArrayBuffer housing the document’s content. The method returns a promise that resolves to the ArrayBuffer or is rejected if an error arises:

    export class AppComponent {

    title = 'PSPDFKit for Web Angular Example';

    ngAfterViewInit(): void {

    PSPDFKit.load({

    // Use the assets directory URL as a base URL. Nutrient will download its library assets from here.

    baseUrl: location.protocol + '//' + location.host + '/assets/',

    document: '/assets/example.pdf',

    container: '.pspdfkit-container',

    })

    .then((instance) => {

    // Store the Nutrient instance in a global variable for later use.

    (window as any).instance = instance;

    // Sign the document when Nutrient is loaded.

    instance.signDocument(null, generatePKCS7)

    .then(() => {

    console.log('Document signed.');

    })

    .catch((error) => {

    console.error('The document could not be signed.', error);

    });

    })

    .catch((error) => {

    console.error(error.message);

    });

    }

    }

    This code initiates the signing process. Upon successful completion, you’ll see the message ‘Document signed.’ in the console. In case of errors during the signing process, an error message will be logged.

    After successfully building and implementing this code, the signing process will run automatically, and the document will reload with the freshly added digital signature.

    Here’s the full code for app.component.ts for your reference:

    import { Component } from '@angular/core';

    import PSPDFKit from 'pspdfkit';

    import * as forge from 'node-forge';

    @Component({

    selector: 'app-root',

    templateUrl: './app.component.html',

    styleUrls: ['./app.component.css'],

    standalone: true,

    })

    export class AppComponent {

    title = 'PSPDFKit for Web Angular Example';

    ngAfterViewInit(): void {

    PSPDFKit.load({

    // Use the assets directory URL as a base URL. Nutrient will download its library assets from here.

    baseUrl: location.protocol + '//' + location.host + '/assets/',

    document: '/assets/example.pdf',

    container: '.pspdfkit-container',

    })

    .then((instance) => {

    // Store the Nutrient instance in a global variable for later use.

    (window as any).instance = instance;

    // Sign the document when Nutrient is loaded.

    instance.signDocument(null, generatePKCS7)

    .then(() => {

    console.log('Document signed.');

    })

    .catch((error) => {

    console.error('The document could not be signed.', error);

    });

    })

    .catch((error) => {

    console.error(error.message);

    });

    }

    }

    function generatePKCS7({

    fileContents,

    }: {

    fileContents: ArrayBuffer | null;

    }): Promise<ArrayBuffer> {

    // Fetch the certificate and private key.

    const certificatePromise = fetch('cert.pem').then((response) =>

    response.text()

    );

    const privateKeyPromise = fetch('private-key.pem').then((response) =>

    response.text()

    );

    return new Promise((resolve, reject) => {

    Promise.all([certificatePromise, privateKeyPromise])

    .then(([certificatePem, privateKeyPem]) => {

    // Parse the certificate and private key using Forge.js.

    const certificate = forge.pki.certificateFromPem(certificatePem);

    const privateKey = forge.pki.privateKeyFromPem(privateKeyPem);

    // Create a PKCS7 signature.

    const p7 = forge.pkcs7.createSignedData();

    if (!fileContents) {

    throw new Error('No file contents provided.');

    }

    const buffer = forge.util.createBuffer(fileContents);

    p7.content = buffer.getBytes();

    p7.addCertificate(certificate);

    // Add the signer information.

    p7.addSigner({

    key: privateKey,

    certificate: certificate,

    digestAlgorithm: forge.pki.oids['sha256'],

    authenticatedAttributes: [

    {

    type: forge.pki.oids['contentType'],

    value: forge.pki.oids['data'],

    },

    {

    type: forge.pki.oids['messageDigest'],

    },

    {

    type: forge.pki.oids['signingTime'],

    value: new Date().toISOString(),

    },

    ],

    });

    // Sign the data.

    p7.sign({ detached: true });

    // Convert the result to an `ArrayBuffer`.

    const result = stringToArrayBuffer(

    forge.asn1.toDer(p7.toAsn1()).getBytes()

    );

    resolve(result);

    })

    .catch(reject);

    });

    }

    function stringToArrayBuffer(binaryString: string): ArrayBuffer {

    const buffer = new ArrayBuffer(binaryString.length);

    let bufferView = new Uint8Array(buffer);

    for (let i = 0, len = binaryString.length; i < len; i++) {

    bufferView[i] = binaryString.charCodeAt(i);

    }

    return buffer;

    }

    We recently added support for CAdES(opens in a new tab)-based signatures, which are advanced digital signatures. To learn more about them, check out our guide.

    Conclusion

    In this blog post, you learned how to add digital signatures to PDF documents using Angular and Nutrient Web SDK. If you’re interested in exploring Nutrient further, you can request a free trial of our SDK or visit our demo page to experience the capabilities of our product firsthand.

    Explore related topics

    Try for free Ready to get started?

    Related SDK articles

    Explore more