EmbedPDF

Attachments

A PDF can carry other files inside it: a spreadsheet with the numbers behind a report, the original of a scanned contract. PDF apps list them in an attachments panel.

const { attachments } = await doc.attachments.list();
 
for (const attachment of attachments) {
  console.log(attachment.name, attachment.size);
}
 
const bytes = await doc.attachments.download(attachments[0].ref);

List#

list() returns the attached files, sorted by name. It doesn’t read the files themselves, so it’s quick however large they are.

FieldWhat it is
refThe attachment’s address. Pass it to download() and delete().
nameThe file name, such as 'figures.xlsx'.
mimeTypeThe file type, such as 'application/pdf', or null.
descriptionA description, or null.
sizeThe size in bytes, or null.
checksumThe file’s MD5 checksum, as lowercase hex, or null.
createdAtWhen the file was created, as an ISO date, or null.
modifiedAtWhen the file was last changed, as an ISO date, or null.
indexIts position in the list. Changes when files are added or deleted.

A field reads null when the PDF doesn’t say.

Download one#

download(ref) returns the file’s bytes. Its name and type are the ones the list gave you, so handing it to the user takes both:

const bytes = await doc.attachments.download(attachment.ref);
 
const url = URL.createObjectURL(new Blob([bytes], { type: attachment.mimeType ?? '' }));
const link = document.createElement('a');
link.href = url;
link.download = attachment.name;
link.click();
URL.revokeObjectURL(url);

A file attachment annotation’s file downloads the same way: page.annotations.downloadResource(ref, 'file') returns its bytes.

Attach a file#

create() takes the bytes and a name, and returns the new attachment:

const { attachment } = await doc.attachments.create({
  data: bytes,
  name: 'figures.xlsx',
  mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  description: 'Numbers for the Q2 report',
});

data is a Uint8Array, an ArrayBuffer or a Blob. A File the user picked brings its own name and type, so it’s enough on its own:

const file = input.files![0];
await doc.attachments.create({ data: file });

Only data and a name are needed; a File brings its name, and a File or Blob brings its type. The engine never guesses a type: without one, mimeType reads null. It records the size, the checksum and the time you attached it.

Names are unique in a document: attaching a second file with a name that’s already there fails with InvalidArg. Rename it and try again.

Delete#

await doc.attachments.delete(attachment.ref);

The attachment disappears from the list. Its bytes stay in earlier versions of the file until you save with mode: 'rewrite'; see Good to know.

Undo#

create() and delete() are changes: each returns its opId in meta, and doc.apply({ undoOf: opId }) undoes it. An undo of a delete brings the same file back, with its bytes, description and dates, and fires attachments.restored. To add or remove several files as one step, apply their ops as one change. See Changes & undo.

const { meta } = await doc.attachments.delete(attachment.ref);
await doc.apply({ undoOf: meta.opId }); // the file is back

An undo leaves alone what someone changed since: the undo of an attach doesn’t delete a file someone else attached under that name since, even with the same bytes, and the undo of a delete doesn’t bring a file back while another file has its name.

Attachments or file-attachment annotations#

A PDF can hold a file in two places:

  • An attachment (this page) belongs to the whole document. PDF apps list it in their attachments panel.
  • A file attachment annotation is pinned to a spot on a page, shown as an icon, and can carry a comment.

They’re separate: doc.attachments.list() doesn’t include files pinned to pages, and deleting an attachment never touches an annotation.

Permissions#

CallNeeds
list()doc.open
download()doc.download: the file leaves the document
create(), delete()doc.attachments.modify

So a user can see which files a document carries without being able to take them out.

Good to know#

Deleted files and saving#

Deleting an attachment removes it from the document as it is now. An incremental save still contains the version you opened, and with it the deleted file. When a file must really be gone, save with mode: 'rewrite'. See Saving.

Names and refs#

An attachment’s ref is its name in the PDF’s list of files, which is why names are unique. When you stored a name yourself, toAttachmentRef('figures.xlsx') turns it back into a ref. A file another app attached may show a different name than the one in its ref; always pass the ref.

Adding or deleting a file changes the index of the files after it in name order, never their refs.

Large files#

The browser holds a file it downloads in memory, so the local engine refuses a file that decodes to more than ATTACHMENT_LIMITS.decodedBytes (256 MB) with PayloadTooLarge. That goes for attachments and file attachment annotations alike; details.limit names the limit. A file’s own size can’t be trusted for this: a small compressed file can decode to gigabytes.

Events#

Other code can follow changes with the attachments.created, attachments.deleted and attachments.restored events. See Events.

Was this page helpful?

Your feedback goes directly to the documentation team.