Pdf-lib: Extract attachments from existing PDF file

Created on 16 Jul 2020  路  3Comments  路  Source: Hopding/pdf-lib

I Have pdf file with a pdf attachment file into it. How can I extract the attachment pdf-file from the existing "main" pdf with pdf-lib as a new pdf file?

Thanks

Most helpful comment

I should note that it would be pretty neat to have a high level API for this:

const pdfDoc = await PDFDocument.load(...)
const attachments = pdfDoc.getAttachments()
const csv = attachments.find(({ name }) => name === 'cars.csv')
fs.writeFileSync(csv.name, csv.data)

If anybody would like to help out, it shouldn't be too difficult to implement with the example I created as a starting point.

All 3 comments

Hello @nachum37!

Here's an example script demonstrating how to do this with pdf-lib:

import fs from 'fs';
import fetch from 'node-fetch';
import {
  PDFDocument,
  PDFName,
  PDFDict,
  PDFArray,
  PDFHexString,
  PDFString,
  PDFStream,
  decodePDFRawStream,
  PDFRawStream,
} from 'pdf-lib';

const extractRawAttachments = (pdfDoc: PDFDocument) => {
  if (!pdfDoc.catalog.has(PDFName.of('Names'))) return [];
  const Names = pdfDoc.catalog.lookup(PDFName.of('Names'), PDFDict);

  if (!Names.has(PDFName.of('EmbeddedFiles'))) return [];
  const EmbeddedFiles = Names.lookup(PDFName.of('EmbeddedFiles'), PDFDict);

  if (!EmbeddedFiles.has(PDFName.of('Names'))) return [];
  const EFNames = EmbeddedFiles.lookup(PDFName.of('Names'), PDFArray);

  const rawAttachments = [];
  for (let idx = 0, len = EFNames.size(); idx < len; idx += 2) {
    const fileName = EFNames.lookup(idx) as PDFHexString | PDFString;
    const fileSpec = EFNames.lookup(idx + 1, PDFDict);
    rawAttachments.push({ fileName, fileSpec });
  }

  return rawAttachments;
};

const extractAttachments = (pdfDoc: PDFDocument) => {
  const rawAttachments = extractRawAttachments(pdfDoc);
  return rawAttachments.map(({ fileName, fileSpec }) => {
    const stream = fileSpec
      .lookup(PDFName.of('EF'), PDFDict)
      .lookup(PDFName.of('F'), PDFStream) as PDFRawStream;
    return {
      name: fileName.decodeText(),
      data: decodePDFRawStream(stream).decode(),
    };
  });
};

(async () => {
  const pdfWithAttachments = await fetch(
    'https://github.com/Hopding/pdf-lib/files/4963252/with_attachment.pdf',
  ).then((res) => res.arrayBuffer());

  const pdfDoc = await PDFDocument.load(pdfWithAttachments);

  const attachments = extractAttachments(pdfDoc);

  const csv = attachments.find((attachment) => attachment.name === 'cars.csv')!;
  fs.writeFileSync('cars.csv', csv.data);
  console.log('CSV file written to ./cars.csv');

  const jpg = attachments.find((attachment) => attachment.name === 'mini.jpg')!;
  fs.writeFileSync('mini.jpg', jpg.data);
  console.log('JPG file written to ./mini.jpg');
})();

Note that this script is written for Node and will write the extracted attachments directly to the filesystem. However, the script will also work in a browser if you remove the fs.writeFileSync lines. You could, for example, replace them with file download prompts or send the attachments to an API.

I hope this helps!

I should note that it would be pretty neat to have a high level API for this:

const pdfDoc = await PDFDocument.load(...)
const attachments = pdfDoc.getAttachments()
const csv = attachments.find(({ name }) => name === 'cars.csv')
fs.writeFileSync(csv.name, csv.data)

If anybody would like to help out, it shouldn't be too difficult to implement with the example I created as a starting point.

Wow. @Hopding, Thanks for the great work and time you spent on writing this code.

There is a way to "read" (decrypt) password-locked PDF with pdf-lib right now?
My original goal was to decrypt my bank pdf mail attachments, they attach the original unlocked pdf to itself, so I need to decrypt it first. this is possible with some node CLI tools wrappers, like pdf-tk, not with vanilla js, so it is impossible to use them in Browser client-side, or Google Apps Script (my original idea). So I need to post them to AWS lambada layers, a way less simple than pdf-lib.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

matthopson picture matthopson  路  4Comments

lepidotteri picture lepidotteri  路  3Comments

faxemaxee picture faxemaxee  路  6Comments

DanielJackson-Oslo picture DanielJackson-Oslo  路  3Comments

DanielJackson-Oslo picture DanielJackson-Oslo  路  8Comments