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.
| Field | What it is |
|---|---|
ref | The attachment’s address. Pass it to download() and delete(). |
name | The file name, such as 'figures.xlsx'. |
mimeType | The file type, such as 'application/pdf', or null. |
description | A description, or null. |
size | The size in bytes, or null. |
checksum | The file’s MD5 checksum, as lowercase hex, or null. |
createdAt | When the file was created, as an ISO date, or null. |
modifiedAt | When the file was last changed, as an ISO date, or null. |
index | Its 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 backAn 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#
| Call | Needs |
|---|---|
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.
Your feedback goes directly to the documentation team.