---
title: "How to extract text from PDFs server-side with PDF.js in Node.js"
canonical_url: "https://www.nutrient.io/blog/pdfjs-server-side-text-extraction/"
md_url: "https://www.nutrient.io/blog/pdfjs-server-side-text-extraction.md"
last_updated: "2026-07-10T16:52:53.441Z"
description: "Learn how to use pdfjs-dist in Node.js for server-side PDF text extraction. Covers basic extraction, TextContent structure, parallel page processing, and CMap configuration."
---

**TL;DR**

PDF.js’s `pdfjs-dist` package runs in Node.js and can extract structured text from PDFs without rendering. The setup has four moving parts:

- Load the document with `getDocument({ data })`, passing a `Uint8Array` from your `Buffer`.

- Resolve `standardFontDataUrl` and `cMapUrl` from the `pdfjs-dist` package on disk.

- Walk pages and call `page.getTextContent()` — filter for items with `"str" in item`.

- Skip the worker configuration (no-op in Node.js) and polyfill `Promise.withResolvers` if your Node version lacks it.

For parallel processing, wrap each page in a promise and `Promise.all`. If you’d rather skip the path-resolution dance, [Nutrient Web SDK](https://www.nutrient.io/sdk/web-overview/) exposes `textLinesForPageIndex` in one call and Document Engine handles batch extraction over REST (representational state transfer).

PDF.js isn’t just for browsers. You can use `pdfjs-dist` in Node.js to extract text from PDFs without any rendering. This is useful for search indexing and content analysis.

## Install

Install the `pdfjs-dist` package:

```bash

npm install pdfjs-dist

```

## Basic text extraction

The following example loads a PDF buffer, walks each page, and returns an array of per-page text strings:

```tsx

import { getDocument, PDFDocumentProxy } from "pdfjs-dist";
import path from "path";
import { createRequire } from "node:module";

// Resolve paths for CMap and font data from the pdfjs-dist package.
const require = createRequire(import.meta.url);
const pdfjsDistPath = require.resolve("pdfjs-dist");
const fontDataPath = path.join(path.resolve(pdfjsDistPath, "../../standard_fonts"), "/");
const cmapPath = path.join(path.resolve(pdfjsDistPath, "../../cmaps"), "/");

async function extractText(pdfBuffer) {
  const uint8Array = new Uint8Array(pdfBuffer);

  const pdf = await getDocument({
    data: uint8Array,
    standardFontDataUrl: fontDataPath,
    cMapUrl: cmapPath,
  }).promise;

  const pageTexts = [];
  for (let i = 1; i <= pdf.numPages; i++) {
    const page = await pdf.getPage(i);
    const textContent = await page.getTextContent();

    const pageText = textContent.items.reduce((text, item) => {
      if ("str" in item && "hasEOL" in item) {
        return text + item.str + (item.hasEOL? "\n" : "");
      }
      return text;
    }, "");

    pageTexts.push(pageText);
  }

  return pageTexts;
}

```

## Understanding TextContent items

`page.getTextContent()` returns an object with an `items` array. Each item has:

```ts

{
  str: "Hello world",   // The text string.
  hasEOL: true,         // Whether it's followed by a line break.
  dir: "ltr",           // Text direction.
  width: 120.5,         // Width in PDF points.
  height: 12,           // Height in PDF points.
  transform: [12, 0, 0, 12, 72, 700], // Position/transform matrix.
}

```

Filter for items with `str` and `hasEOL` — some items are positioning markers without text.

## Parallel page processing

For large PDFs, extract pages in parallel. The snippet below uses the `pdf` document from the basic example above:

```tsx

const textPromises = [];
for (let i = 1; i <= pdf.numPages; i++) {
  textPromises.push(
    pdf.getPage(i).then((page) => page.getTextContent()).then((textContent) =>
        textContent.items.reduce((text, item) => {
          if ("str" in item && "hasEOL" in item) {
            return text + item.str + (item.hasEOL? "\n" : "");
          }
          return text;
        }, ""),
      ),
  );
}
const allTexts = await Promise.all(textPromises);

```

## Configuration notes

PDF.js requires three adjustments when running in Node.js that differ from a browser setup.

### CMap and font paths

In Node.js, you need to resolve these paths from the `pdfjs-dist` package itself:

```tsx

const pdfjsDistPath = require.resolve("pdfjs-dist");
const fontDataPath = path.join(path.resolve(pdfjsDistPath, "../../standard_fonts"), "/");
const cmapPath = path.join(path.resolve(pdfjsDistPath, "../../cmaps"), "/");

```

These are directory paths to the bundled CMap and font files — required for proper text extraction from Chinese, Japanese, and Korean (CJK) and non-standard fonts.

### No worker needed

In Node.js, you don’t need to configure `GlobalWorkerOptions.workerSrc`. PDF.js falls back to running in the main thread, which is fine for server-side batch processing.

### Polyfill

If your Node.js version doesn’t support `Promise.withResolvers`, add a polyfill:

```tsx

import "core-js/proposals/promise-with-resolvers";

```

## Key points

- `pdfjs-dist` works in Node.js without a browser or canvas.

- `getDocument({ data })` accepts `Uint8Array` from a `Buffer`.

- `page.getTextContent()` gives you structured text with position metadata.

- Filter text items for `"str" in item` to skip non-text markers.

- No worker configuration is needed for server-side use.

- Always configure CMap and standard font paths for proper text extraction.

- Text extraction is the foundation for search indexing and content analysis.

## FAQ

#### Can I really run PDF.js in Node.js without a browser?

Yes. `pdfjs-dist` works in Node.js for text extraction, metadata reading, and structural inspection. The catch is that rendering (canvas painting) requires either a browser environment or an emulated canvas package, so use `pdf-lib`, a headless browser, or a server-side renderer for that. Text extraction itself has no rendering requirement.

#### Why do I need to resolve CMap and font paths manually in Node?

In the browser, you typically point `cMapUrl` and `standardFontDataUrl` at a content delivery network (CDN). In Node.js, there’s no URL to fetch from, so you have to resolve the bundled `cmaps` and `standard_fonts` directories from inside the `pdfjs-dist` package. Without them, text extraction from PDFs with CJK or embedded standard fonts will return blanks or mojibake.

#### What is <code>Promise.withResolvers</code> and why do I need to polyfill it?

`Promise.withResolvers` is a recent JavaScript API (Stage 4, available in Node 22+) that PDF.js uses internally. On older Node.js versions, you’ll get a `ReferenceError` at import time. The `core-js/proposals/promise-with-resolvers` polyfill adds it. Node 22+ users can skip the polyfill.

#### Why are some <code>textContent.items</code> missing the <code>str</code> field?

`getTextContent()` returns a mixed array: Most items are text fragments with a `str` field, but some are positioning markers used internally to recreate the page’s reading order. Filter with `"str" in item` to skip them. The `hasEOL` flag tells you when an item should be followed by a line break in your assembled output.

#### Do I need to call <code>worker.destroy()</code> or <code>loadingTask.destroy()</code> after extraction?

Yes, for long-running processes. PDF.js holds the parsed document tree in memory, and on a busy server, you’ll leak across requests if you don’t release it. Call `await loadingTask.destroy()` (or `pdf.destroy()`) when you’re done with each PDF. For short-lived scripts, process exit cleans up automatically.

#### How do Nutrient Web SDK and Document Engine compare?

Nutrient Web SDK exposes `instance.textLinesForPageIndex(pageIndex)` for client-side extraction with one call — no CMap setup, no item filtering. For server-side batch jobs, [Document Engine](https://www.nutrient.io/sdk/document-engine/) provides a REST API for text extraction, OCR, redaction, and content analysis without bundling PDF.js into your service.

## How Nutrient Web SDK handles this

No Node.js PDF.js setup, no CMap path resolution, and no polyfills are required. Nutrient Web SDK extracts text from any page with a single API call:

```js

// Extract text from a page — one line.
const textLines = await instance.textLinesForPageIndex(0);
console.log(textLines.map((line) => line.contents).join("\n"));

```

For server-side processing, [Nutrient Document Engine](https://www.nutrient.io/sdk/document-engine/) provides text extraction, optical character recognition ([OCR](https://www.nutrient.io/guides/document-engine/ocr.md)), and content analysis through a REST API, with no extra binary dependencies on the host.

---

_See [Nutrient Web SDK](https://www.nutrient.io/sdk/web-overview/) for an alternative to wiring up PDF.js in Node. The [text extraction](https://www.nutrient.io/guides/web/features/text-extraction.md) and [OCR](https://www.nutrient.io/guides/web/ocr.md) guides cover the API in depth. Follow the [migration guide](https://www.nutrient.io/guides/web/about/migration-guides/migrating-from-mozilla-pdfjs.md) to switch, or [talk to Sales](https://www.nutrient.io/contact-sales/?=sdk) about your requirements._
---

## Related pages

- [Accessibility Untangled Why It Matters Guide](/blog/accessibility-untangled-why-it-matters-guide.md)
- [The business case for accessibility: Five ways it drives enterprise value](/blog/5-ways-accessibility-drives-enterprise-value.md)
- [Advanced Techniques For React Native Ui Components](/blog/advanced-techniques-for-react-native-ui-components.md)
- [Angular File Viewer Pdf Image Office Files](/blog/angular-file-viewer-pdf-image-office-files.md)
- [Ai Document Automation Extraction To Action](/blog/ai-document-automation-extraction-to-action.md)
- [Auto Tagging And Document Accessibility In Dotnet Sdk](/blog/auto-tagging-and-document-accessibility-in-dotnet-sdk.md)
- [Ai Legal Assistant Document Authoring](/blog/ai-legal-assistant-document-authoring.md)
- [The CEO’s AI playbook: Why decision architecture beats model selection](/blog/ceo-ai-playbook-decision-architecture.md)
- [Best Document Viewers](/blog/best-document-viewers.md)
- [Convert One Drive Files To Pdf In Sharepoint](/blog/convert-one-drive-files-to-pdf-in-sharepoint.md)
- [Complete Guide To Pdfjs](/blog/complete-guide-to-pdfjs.md)
- [Construction Document Data Extraction](/blog/construction-document-data-extraction.md)
- [Create And Edit Pdfs In Flutter](/blog/create-and-edit-pdfs-in-flutter.md)
- [Create Pdfs With React](/blog/create-pdfs-with-react.md)
- [Creating A Document Scanner With Ocr In Python](/blog/creating-a-document-scanner-with-ocr-in-python.md)
- [The CTO’s AI playbook: Why accountability architecture beats orchestration](/blog/cto-ai-playbook-accountability-architecture.md)
- [Digital Workflow Automation](/blog/digital-workflow-automation.md)
- [Digital Signatures](/blog/digital-signatures.md)
- [Document Ai Vs Ocr](/blog/document-ai-vs-ocr.md)
- [Document Viewer](/blog/document-viewer.md)
- [app.py](/blog/extract-text-from-pdf-using-python.md)
- [How To Build A Dotnet Maui Pdf Viewer](/blog/how-to-build-a-dotnet-maui-pdf-viewer.md)
- [Emerging threats: Your logging system may be an agentic threat vector](/blog/emerging-threats-your-logging-system.md)
- [How To Build A Flutter Pdf Viewer](/blog/how-to-build-a-flutter-pdf-viewer.md)
- [How To Build A Javascript Pdf Viewer](/blog/how-to-build-a-javascript-pdf-viewer.md)
- [or](/blog/how-to-build-a-javascript-pdf-viewer-with-pdfjs.md)
- [How To Build A React Powerpoint Viewer](/blog/how-to-build-a-react-powerpoint-viewer.md)
- [or](/blog/how-to-build-a-nextjs-pdf-viewer.md)
- [How To Build A Powerpoint Viewer Using Javascript](/blog/how-to-build-a-powerpoint-viewer-using-javascript.md)
- [How To Build A React Native Pdf Viewer](/blog/how-to-build-a-react-native-pdf-viewer.md)
- [How To Build A Reactjs File Viewer](/blog/how-to-build-a-reactjs-file-viewer.md)
- [Using Yarn](/blog/how-to-build-a-react-excel-viewer.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer-with-react-pdf.md)
- [How To Build A Vuejs Pdf Viewer With Pdfjs](/blog/how-to-build-a-vuejs-pdf-viewer-with-pdfjs.md)
- [How To Build A Reactjs Viewer With Pdfjs](/blog/how-to-build-a-reactjs-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer](/blog/how-to-build-a-vuejs-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Pdfjs](/blog/how-to-build-an-angular-pdf-viewer-with-pdfjs.md)
- [How To Build An Angular Pdf Viewer With Ng2 Pdf Viewer](/blog/how-to-build-an-angular-pdf-viewer-with-ng2-pdf-viewer.md)
- [How To Convert Html To Pdf Using Html2pdf](/blog/how-to-convert-html-to-pdf-using-html2pdf.md)
- [How To Build An Android Pdf Viewer](/blog/how-to-build-an-android-pdf-viewer.md)
- [or](/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-python.md)
- [or](/blog/how-to-convert-html-to-pdf-using-react.md)
- [How To Convert Word To Pdf In Nodejs](/blog/how-to-convert-word-to-pdf-in-nodejs.md)
- [How To Create Pdfs With React To Pdf](/blog/how-to-create-pdfs-with-react-to-pdf.md)
- [How To Edit Pdfs Using Ios Pdf Library](/blog/how-to-edit-pdfs-using-ios-pdf-library.md)
- [How To Embed A Pdf Viewer In Your Website](/blog/how-to-embed-a-pdf-viewer-in-your-website.md)
- [base_url tells WeasyPrint where to resolve relative asset paths](/blog/how-to-generate-pdf-reports-from-html-in-python.md)
- [How To Generate Pdf From Html With Nodejs](/blog/how-to-generate-pdf-from-html-with-nodejs.md)
- [From an HTML string.](/blog/html-in-pdf-format.md)
- [Swift Package Manager](/blog/mobile-pdf-sdk.md)
- [Open an image.](/blog/how-to-use-tesseract-ocr-in-python.md)
- [Linearized Pdf](/blog/linearized-pdf.md)
- [`elements` come from your document parser — each has a type and content.](/blog/multimodal-rag.md)
- [Nutrient Vs Conga Composer](/blog/nutrient-vs-conga-composer.md)
- [Open Pdf In Your Web App](/blog/open-pdf-in-your-web-app.md)
- [Pdf Page Labels](/blog/pdf-page-labels.md)
- [Online Document Viewer](/blog/online-document-viewer.md)
- [Pdf Sdk Performance Benchmark](/blog/pdf-sdk-performance-benchmark.md)
- [Pdfjs Annotation Editor Layer](/blog/pdfjs-annotation-editor-layer.md)
- [Pdf Ua Compliance Guide](/blog/pdf-ua-compliance-guide.md)
- [Pdf Sdk Compliance Security Checklist](/blog/pdf-sdk-compliance-security-checklist.md)
- [Pdfjs Area Annotations Canvas Capture](/blog/pdfjs-area-annotations-canvas-capture.md)
- [Pdfjs Coordinate Systems Pdf To Screen](/blog/pdfjs-coordinate-systems-pdf-to-screen.md)
- [Pdfjs Eventbus Guide](/blog/pdfjs-eventbus-guide.md)
- [Pdfjs Navigation Zoom Rotation](/blog/pdfjs-navigation-zoom-rotation.md)
- [Pdfjs Native Annotation Layer Forms](/blog/pdfjs-native-annotation-layer-forms.md)
- [Pdfjs Pdf Page Manipulation Pdf Lib](/blog/pdfjs-pdf-page-manipulation-pdf-lib.md)
- [Pdfjs Limitations Commercial Upgrade](/blog/pdfjs-limitations-commercial-upgrade.md)
- [Pdfjs Sticky Note Annotations](/blog/pdfjs-sticky-note-annotations.md)
- [Pdfjs React Viewer Setup](/blog/pdfjs-react-viewer-setup.md)
- [Pdfjs Rendering Overlays React Portals](/blog/pdfjs-rendering-overlays-react-portals.md)
- [Pdfjs Thumbnail Sidebar](/blog/pdfjs-thumbnail-sidebar.md)
- [Pdfjs Text Highlight Annotations](/blog/pdfjs-text-highlight-annotations.md)
- [Pdfjs Text Search Pdffindcontroller](/blog/pdfjs-text-search-pdffindcontroller.md)
- [Process Flows](/blog/process-flows.md)
- [or](/blog/sample-blog-updated.md)
- [React Native Pdf Annotation](/blog/react-native-pdf-annotation.md)
- [Open an image file.](/blog/tesseract-python-guide.md)
- [Convert an HTML file to PDF.](/blog/top-ten-ways-to-convert-html-to-pdf.md)
- [Vector Pdf](/blog/vector-pdf.md)
- [Top 5 Javascript Pdf Viewers](/blog/top-5-javascript-pdf-viewers.md)
- [Web Sdk Is Now Headless](/blog/web-sdk-is-now-headless.md)
- [Wcag2 Accessibility Requirements Documents](/blog/wcag2-accessibility-requirements-documents.md)
- [What Is A Vpat](/blog/what-is-a-vpat.md)
- [What Are Annotations](/blog/what-are-annotations.md)
- [Why Your Ai Agent Hallucinates Pdf Table Data](/blog/why-your-ai-agent-hallucinates-pdf-table-data.md)
- [What Is Pdf Ua](/blog/what-is-pdf-ua.md)
- [Why Pdfium Is A Trusted Platform For Pdf Rendering](/blog/why-pdfium-is-a-trusted-platform-for-pdf-rendering.md)

