Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [BREAKING CHANGE] Remove the virtual file system (`pdfkit/virtual-fs`). Browser builds no longer depend on `fs`: use `registerFile` to register `Uint8Array` data under a path, pass a `Uint8Array` or `ArrayBuffer` directly to `registerFont`, `image` and `file`, or pass a data URL directly to `image` and `file`
- Add `registerFile(path, data, options)` to globally register in-memory files in Node and browsers, with optional `birthtime` and `ctime` metadata. Passing `undefined` as data unregisters the path
- Add experimental `toBlob(document)` and `toBytes(document)` output helpers under `pdfkit/output`
- Accept already-parsed fontkit `Font` instances in `doc.font()` and `registerFont`
- Load the PDF/A ICC profile from disk only when needed in Node, while continuing to bundle it in browser builds
- [BREAKING CHANGE] Restrict AcroForm options to documented mappings and explicit escape hatches.
- [BREAKING CHANGE] Stop automatically uppercasing annotation option keys.
Expand Down
4 changes: 3 additions & 1 deletion docs/text.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,9 @@ and Datafork TrueType (`.dfont`) fonts.

To change the font used to render text, just call the `font` method. If you
are using a standard PDF font, just pass the name to the `font` method.
Otherwise, pass the path to the font file, or a `Buffer` containing the font data.
Otherwise, pass the path to the font file, a `Buffer` containing the font data,
or an already-parsed [fontkit](https://github.com/foliojs/fontkit) `Font`
instance.
Comment thread
diegomura marked this conversation as resolved.
If the font is a collection font (`.ttc` and `.dfont` files), meaning that it
contains multiple styles in the same file, you should pass the name of the style
to be extracted from the collection.
Expand Down
3 changes: 3 additions & 0 deletions lib/font_factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class PDFFontFactory {
font = create(src, family);
} else if (src instanceof ArrayBuffer) {
font = create(new Uint8Array(src), family);
} else if (typeof src?.layout === 'function') {
// already-parsed fontkit font instance
font = src;
}
Comment thread
diegomura marked this conversation as resolved.

if (font == null) {
Expand Down
55 changes: 55 additions & 0 deletions tests/unit/font.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { vi } from 'vitest';
import { readFileSync } from 'fs';
import { create } from 'fontkit';
import PDFDocument from '../../lib/document';
import PDFFontFactory from '../../lib/font_factory';
import StandardFont from '../../lib/font/standard';
Expand All @@ -22,6 +23,60 @@ describe('PDFFontFactory', () => {
});
});

describe('fontkit font instance sources', () => {
const parseFont = (file = 'tests/fonts/Roboto-Regular.ttf') =>
create(readFileSync(file));

test('doc.font() embeds a parsed font instance and renders text', () => {
const doc = new PDFDocument({ compress: false });
doc.font(parseFont()).text('parsed roboto');

const pdf = collectPdf(doc);

expect(pdf).toContain('Roboto-Regular');
expect(pdf).toContain('startxref');
expect(missingObjects(pdf)).toHaveLength(0);
});

test('the same instance passed twice reuses the embedded font', () => {
const doc = new PDFDocument({ font: null });
const font = parseFont();

doc.font(font);
const first = doc._font;
const familyCount = Object.keys(doc._fontFamilies).length;

doc.font(font);

expect(doc._font).toBe(first);
expect(Object.keys(doc._fontFamilies)).toHaveLength(familyCount);
});

test('different instances do not collide', () => {
const doc = new PDFDocument({ font: null });

doc.font(parseFont('tests/fonts/Roboto-Regular.ttf'));
const regular = doc._font;
doc.font(parseFont('tests/fonts/Roboto-Italic.ttf'));
const italic = doc._font;

expect(italic).not.toBe(regular);
expect(doc._fontFamilies['Roboto-Regular']).toBe(regular);
expect(doc._fontFamilies['Roboto-Italic']).toBe(italic);
});

test('registerFont accepts a parsed font instance', () => {
const doc = new PDFDocument({ compress: false });
doc.registerFont('MyFont', parseFont());
doc.font('MyFont').text('registered parsed font');

expect(doc._fontFamilies['MyFont']).toBe(doc._font);

const pdf = collectPdf(doc);
expect(missingObjects(pdf)).toHaveLength(0);
});
});
Comment on lines +75 to +78

describe('EmbeddedFont', () => {
test('no fontLayoutCache option', () => {
const document = new PDFDocument();
Expand Down
Loading