pdf-parse and pdf2json are two popular Node.js libraries used to extract content from PDF files, but they serve different primary purposes. pdf-parse is a lightweight wrapper around pdf.js designed specifically for extracting raw text content from PDF pages with minimal configuration. It returns a simple JSON object containing text per page, making it ideal for search indexing or basic text analysis. pdf2json, on the other hand, converts the entire PDF structure into a detailed JSON representation, preserving not just text but also layout coordinates, font information, and form field data. This makes pdf2json better suited for applications requiring precise layout reconstruction, form processing, or visual analysis of the document structure.
When working with PDFs in a Node.js environment, developers often face a choice between extracting raw text for analysis or parsing the full document structure for layout preservation. pdf-parse and pdf2json address these two distinct needs. While both libraries run on the server and handle PDF processing, their output formats and underlying capabilities differ significantly. Let's explore how they compare in real-world scenarios.
pdf-parse is built for one thing: getting text out of a PDF as quickly and easily as possible. It acts as a thin wrapper around Mozilla's pdf.js library, stripping away the complexity of the rendering engine to give you a clean string of text per page. It does not care about where the text sits on the page or what font was used.
const pdf = require('pdf-parse');
const fs = require('fs');
const dataBuffer = fs.readFileSync('document.pdf');
pdf(dataBuffer).then(function(data) {
// data.text contains the full text of the PDF
// data.numpages gives the page count
// data.info contains metadata like title and author
console.log(data.text);
});
pdf2json takes a different approach. It parses the PDF into a structured JSON object that represents the visual layout. Every text element includes its x/y coordinates, font size, and rotation. This allows developers to rebuild the document visually or extract data based on position rather than just content flow.
const PDFParser = require('pdf2json');
const pdfParser = new PDFParser();
pdfParser.on('pdfParser_dataReady', (pdfData) => {
// pdfData.formImage.Pages contains an array of page objects
// Each page has 'Texts' array with x, y, w, h, and R (rotation) for every snippet
console.log(JSON.stringify(pdfData.formImage.Pages[0].Texts));
});
pdfParser.loadPDF('document.pdf');
One of the biggest differences lies in how they handle spatial information. If you are building a tool that needs to highlight specific words on a rendered canvas or extract data from a specific column in a table, coordinates are essential.
pdf-parse discards this information entirely. The page object in its response only contains the textContent and a count of lines if requested, but no positional data. You cannot determine where a specific sentence appears on the page.
// pdf-parse output structure
// No x/y coordinates available
const result = await pdf(dataBuffer);
const pageOne = result.pages[0];
// pageOne only has: { textContent: "...", lines: [...] }
pdf2json excels here. Every text fragment comes with precise x, y, w (width), and h (height) properties relative to the page dimensions. This is critical for applications like invoice processing where "Total Amount" is always found at a specific coordinate.
// pdf2json output structure
// Detailed coordinates for every text run
const texts = pdfData.formImage.Pages[0].Texts;
texts.forEach(textRun => {
// textRun.x, textRun.y, textRun.w, textRun.h are available
if (textRun.x > 400 && textRun.y < 100) {
// Logic for top-right corner content
}
});
If your workflow involves processing filled-out PDF forms (FDF/AcroForms), the choice becomes even clearer.
pdf-parse generally ignores form field data. It treats the visual representation of the filled text as regular page content. If a user types into a form field, pdf-parse might pick it up as part of the general text stream, but it won't identify it as a specific field named "email_address" or "signature".
// pdf-parse cannot distinguish form fields from static text
// You get a blob of text without field names
const data = await pdf(formBuffer);
// data.text includes the filled values but no mapping to field IDs
pdf2json was originally designed with form processing in mind. It explicitly parses form fields and includes them in the JSON output, often separating them from the static page text. This allows you to map values directly to database columns based on field names.
// pdf2json identifies form fields specifically
// Fields are often accessible via the 'Fields' property in the page object
const fields = pdfData.formImage.Pages[0].Fields;
fields.forEach(field => {
// field.Name, field.Value, field.Type are available
console.log(`${field.Name}: ${field.Value}`);
});
Simplicity is a major factor for quick scripts. pdf-parse offers a zero-config experience. You pass a buffer or a file path, and you get text. There are no event listeners to manage or complex options to set for standard use cases.
// Simple async/await pattern
async function extractText(path) {
const buffer = fs.readFileSync(path);
const data = await pdf(buffer);
return data.text;
}
pdf2json uses an event-driven architecture, which can be slightly more verbose for simple tasks. You must instantiate the parser, attach a listener for the pdfParser_dataReady event, and handle errors separately. This pattern is powerful for streaming large files but adds boilerplate for simple scripts.
// Event-based pattern required
function extractStructure(path) {
return new Promise((resolve, reject) => {
const parser = new PDFParser();
parser.on('pdfParser_dataReady', resolve);
parser.on('pdfParser_dataError', reject);
parser.loadPDF(path);
});
}
Because pdf-parse stops processing once it extracts the text strings, it generally consumes less memory and CPU for simple text extraction tasks. It does not build the heavy internal representation of the page layout.
pdf2json generates a much larger output object because it stores metadata for every single visual element. For a text-heavy document with complex formatting, the resulting JSON from pdf2json can be significantly larger than the raw text from pdf-parse. This trade-off is necessary if you need the layout data, but it is a cost to consider for high-volume processing pipelines where only text matters.
Despite their differences, both libraries share some common traits that make them viable for server-side Node.js applications.
Both libraries are designed primarily for Node.js environments. While pdf.js (the engine behind pdf-parse) runs in browsers, pdf-parse wraps it specifically for server usage without DOM dependencies. pdf2json is also a pure Node.js module.
// Both work seamlessly in Express.js routes
app.post('/upload', async (req, res) => {
const buffer = req.file.buffer;
// Can use either library here depending on needs
});
Both libraries handle multi-page documents naturally. They return data structures that iterate over pages, allowing you to process documents of any length without manual splitting.
// pdf-parse
data.pages.forEach(page => console.log(page.textContent));
// pdf2json
pdfData.formImage.Pages.forEach(page => console.log(page.Texts));
Unlike some PDF tools that require installing system-level binaries like poppler or ghostscript, both pdf-parse and pdf2json are pure JavaScript (or bundle their own WASM/JS engines). This makes deployment to serverless environments like AWS Lambda or Vercel much simpler.
# No need for apt-get install poppler-utils
npm install pdf-parse
# or
npm install pdf2json
| Feature | pdf-parse | pdf2json |
|---|---|---|
| Primary Output | Raw text strings | Structured JSON with layout |
| Coordinates | ❌ Not available | ✅ Precise x/y/w/h included |
| Form Fields | ❌ Treated as regular text | ✅ Explicitly parsed and named |
| API Style | Promise-based (async/await) | Event-based (listeners) |
| Output Size | Small (text only) | Large (metadata heavy) |
| Best For | Search, indexing, NLP | Forms, layout analysis, reconstruction |
pdf-parse is your go-to tool when you treat the PDF as a document of words. If you are building a search engine, a chatbot that reads PDFs, or a simple archiving system, this library gives you the text with the least amount of friction. It respects the "content over layout" philosophy.
pdf2json is the specialist for when the position of the text matters as much as the text itself. If you are automating data entry from invoices, processing tax forms, or need to regenerate a visual representation of the PDF in a custom viewer, the detailed structural data it provides is indispensable.
Final Thought: Don't over-engineer your solution. If you just need the words, pdf-parse is faster and simpler. If you need to know exactly where those words live on the page or what form field they belong to, pdf2json is the only choice between the two.
Choose pdf-parse if your goal is simply to extract readable text from a PDF for indexing, searching, or natural language processing. It is the better option when you need a quick, low-memory solution that ignores complex layout details and focuses solely on the textual content. Its API is straightforward and requires no configuration for standard text extraction tasks.
Choose pdf2json if you need to preserve the spatial layout of the document, extract data from filled forms, or analyze the specific positioning of elements on the page. It is the right choice for applications that need to reconstruct the visual structure of the PDF or process interactive form fields, as it provides detailed coordinate and metadata information that pdf-parse discards.
Pure TypeScript, cross-platform module for extracting text, images, and tables from PDFs.
Run 🤗 directly in your browser or in Node!
// v1
// const pdf = require('pdf-parse');
// pdf(buffer).then(result => console.log(result.text));
// v2
const { PDFParse } = require('pdf-parse');
// import { PDFParse } from 'pdf-parse';
async function run() {
const parser = new PDFParse({ url: 'https://bitcoin.org/bitcoin.pdf' });
const result = await parser.getText();
console.log(result.text);
}
run();
React, Vue, Angular, or any other web framework.CLI DocumentationSecurity PolicygetHeadergetInfogetTextgetScreenshotgetImagegetTableunit testsIntegration tests to validate end-to-end behavior across environments.live demo, examples, tests and tests example folders.Next.js + Vercel, Netlify, AWS Lambda, Cloudflare Workers.npm install pdf-parse
# or
pnpm add pdf-parse
# or
yarn add pdf-parse
# or
bun add pdf-parse
For command-line usage, install the package globally:
npm install -g pdf-parse
Or use it directly with npx:
npx pdf-parse --help
For detailed CLI documentation and usage examples, see: CLI Documentation
getHeader — Node Utility: PDF Header Retrieval and Validation// Important: getHeader is available from the 'pdf-parse/node' submodule
import { getHeader } from 'pdf-parse/node';
// Retrieve HTTP headers and file size without downloading the full file.
// Pass `true` to check PDF magic bytes via range request.
// Optionally validates PDFs by fetching the first 4 bytes (magic bytes).
// Useful for checking file existence, size, and type before full parsing.
// Node only, will not work in browser environments.
const result = await getHeader('https://bitcoin.org/bitcoin.pdf', true);
console.log(`Status: ${result.status}`);
console.log(`Content-Length: ${result.size}`);
console.log(`Is PDF: ${result.isPdf}`);
console.log(`Headers:`, result.headers);
getInfo — Extract Metadata and Document Informationimport { readFile } from 'node:fs/promises';
import { PDFParse } from 'pdf-parse';
const link = 'https://mehmet-kozan.github.io/pdf-parse/pdf/climate.pdf';
// const buffer = await readFile('reports/pdf/climate.pdf');
// const parser = new PDFParse({ data: buffer });
const parser = new PDFParse({ url: link });
const result = await parser.getInfo({ parsePageInfo: true });
await parser.destroy();
console.log(`Total pages: ${result.total}`);
console.log(`Title: ${result.info?.Title}`);
console.log(`Author: ${result.info?.Author}`);
console.log(`Creator: ${result.info?.Creator}`);
console.log(`Producer: ${result.info?.Producer}`);
// Access parsed date information
const dates = result.getDateNode();
console.log(`Creation Date: ${dates.CreationDate}`);
console.log(`Modification Date: ${dates.ModDate}`);
// Links, pageLabel, width, height (when `parsePageInfo` is true)
console.log('Per-page information:');
console.log(JSON.stringify(result.pages, null, 2));
getText — Extract Textimport { PDFParse } from 'pdf-parse';
const parser = new PDFParse({ url: 'https://bitcoin.org/bitcoin.pdf' });
const result = await parser.getText();
// to extract text from page 3 only:
// const result = await parser.getText({ partial: [3] });
await parser.destroy();
console.log(result.text);
For a complete list of configuration options, see:
Usage Examples:
password.test.tsspecific-pages.test.tshyperlink.test.tspassword.test.tsurl.test.tsbase64.test.tslarge-file.test.tsgetScreenshot — Render Pages as PNGimport { readFile, writeFile } from 'node:fs/promises';
import { PDFParse } from 'pdf-parse';
const link = 'https://bitcoin.org/bitcoin.pdf';
// const buffer = await readFile('reports/pdf/bitcoin.pdf');
// const parser = new PDFParse({ data: buffer });
const parser = new PDFParse({ url: link });
// scale:1 for original page size.
// scale:1.5 50% bigger.
const result = await parser.getScreenshot({ scale: 1.5 });
await parser.destroy();
await writeFile('bitcoin.png', result.pages[0].data);
Usage Examples:
getScreenshot({scale:1.5}) — Increase rendering scale (higher DPI / larger image)getScreenshot({desiredWidth:1024}) — Request a target width in pixels; height scales to keep aspect ratioimageDataUrl (default: true) — include base64 data URL string in the result.imageBuffer (default: true) — include a binary buffer for each image.partial (e.g. getScreenshot({ partial: [1,3] }))partial overrides first/last.first to render the first N pages (e.g. getScreenshot({ first: 3 })).last to render the last N pages (e.g. getScreenshot({ last: 2 })).first and last are provided they form an inclusive range (first..last).getImage — Extract Embedded Imagesimport { readFile, writeFile } from 'node:fs/promises';
import { PDFParse } from 'pdf-parse';
const link = new URL('https://mehmet-kozan.github.io/pdf-parse/pdf/image-test.pdf');
// const buffer = await readFile('reports/pdf/image-test.pdf');
// const parser = new PDFParse({ data: buffer });
const parser = new PDFParse({ url: link });
const result = await parser.getImage();
await parser.destroy();
await writeFile('adobe.png', result.pages[0].images[0].data);
Usage Examples:
getImage({ imageThreshold: 50 })imageThreshold is 80 (pixels)imageThreshold: 0.imageDataUrl (default: true) — include base64 data URL string in the result.imageBuffer (default: true) — include a binary buffer for each image.getImage({ partial: [2,4] })getTable — Extract Tabular Dataimport { readFile } from 'node:fs/promises';
import { PDFParse } from 'pdf-parse';
const link = new URL('https://mehmet-kozan.github.io/pdf-parse/pdf/simple-table.pdf');
// const buffer = await readFile('reports/pdf/simple-table.pdf');
// const parser = new PDFParse({ data: buffer });
const parser = new PDFParse({ url: link });
const result = await parser.getTable();
await parser.destroy();
// Pretty-print each row of the first table
for (const row of result.pages[0].tables[0]) {
console.log(JSON.stringify(row));
}
import type { LoadParameters, ParseParameters, TextResult } from 'pdf-parse';
import { PasswordException, PDFParse, VerbosityLevel } from 'pdf-parse';
const loadParams: LoadParameters = {
url: 'https://mehmet-kozan.github.io/pdf-parse/pdf/password-123456.pdf',
verbosity: VerbosityLevel.WARNINGS,
password: 'abcdef',
};
const parseParams: ParseParameters = {
first: 1,
};
// Initialize the parser class without executing any code yet
const parser = new PDFParse(loadParams);
function handleResult(result: TextResult) {
console.log(result.text);
}
try {
const result = await parser.getText(parseParams);
handleResult(result);
} catch (error) {
// InvalidPDFException
// PasswordException
// FormatError
// ResponseException
// AbortException
// UnknownErrorException
if (error instanceof PasswordException) {
console.error('Password must be 123456\n', error);
} else {
throw error;
}
} finally {
// Always call destroy() to free memory
await parser.destroy();
}
React, Vue, Angular, or any other web framework.https://mehmet-kozan.github.io/pdf-parse/reports/demopdf-parse.es.js UMD/Global: pdf-parse.umd.jsweb worker explicitly.<!-- ES Module -->
<script type="module">
import {PDFParse} from 'https://cdn.jsdelivr.net/npm/pdf-parse@latest/dist/pdf-parse/web/pdf-parse.es.js';
//// Available Worker Files
// pdf.worker.mjs
// pdf.worker.min.mjs
// If you use a custom build or host pdf.worker.mjs yourself, configure worker accordingly.
PDFParse.setWorker('https://cdn.jsdelivr.net/npm/pdf-parse@latest/dist/pdf-parse/web/pdf.worker.mjs');
const parser = new PDFParse({url:'https://mehmet-kozan.github.io/pdf-parse/pdf/bitcoin.pdf'});
const result = await parser.getText();
console.log(result.text)
</script>
CDN Options: https://www.jsdelivr.com/package/npm/pdf-parse
https://cdn.jsdelivr.net/npm/pdf-parse@latest/dist/pdf-parse/web/pdf-parse.es.jshttps://cdn.jsdelivr.net/npm/pdf-parse@2.4.5/dist/pdf-parse/web/pdf-parse.es.jshttps://cdn.jsdelivr.net/npm/pdf-parse@latest/dist/pdf-parse/web/pdf-parse.umd.jshttps://cdn.jsdelivr.net/npm/pdf-parse@2.4.5/dist/pdf-parse/web/pdf-parse.umd.jsWorker Options:
https://cdn.jsdelivr.net/npm/pdf-parse@latest/dist/pdf-parse/web/pdf.worker.mjshttps://cdn.jsdelivr.net/npm/pdf-parse@latest/dist/pdf-parse/web/pdf.worker.min.mjspdf-parse-basedpdf-parse-basedBenchmark Note: The benchmark currently runs only against
pdf2json. I don't know the current state ofpdf2json— the original reason for creatingpdf-parsewas to work around stability issues withpdf2json. I deliberately did not includepdf-parseor otherpdf.js-based packages in the benchmark because dependencies conflict. If you have recommendations for additional packages to include, please open an issue, seebenchmark results.
Integration tests run on Node.js 20–24, see test_integration.yml.
Requires additional setup see docs/troubleshooting.md.
See docs/troubleshooting.md for detailed troubleshooting steps and worker configuration for Node.js and serverless environments.
If you encounter issues, please refer to the Troubleshooting Guide.
When opening an issue, please attach the relevant PDF file if possible. Providing the file will help us reproduce and resolve your issue more efficiently. For detailed guidelines on how to contribute, report bugs, or submit pull requests, see: contributing to pdf-parse