From 308e4370bb4da2b2168f26e6c09004e801d54b02 Mon Sep 17 00:00:00 2001 From: Haoran Un Date: Tue, 22 Oct 2024 14:02:38 +1100 Subject: [PATCH 1/3] Fix typo in PDFPage.spec.ts --- tests/api/PDFPage.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/api/PDFPage.spec.ts b/tests/api/PDFPage.spec.ts index 3a5f0a949..e02ece789 100644 --- a/tests/api/PDFPage.spec.ts +++ b/tests/api/PDFPage.spec.ts @@ -3,7 +3,7 @@ import { PDFArray, PDFDocument, PDFName, StandardFonts } from 'src/index'; const birdPng = fs.readFileSync('assets/images/greyscale_bird.png'); -describe(`PDFDocument`, () => { +describe(`PDFPage`, () => { describe(`getSize() method`, () => { it(`returns the width and height of the the page's MediaBox`, async () => { const pdfDoc = await PDFDocument.create(); From e7480c34fff3a93236c7794607108711166a3d60 Mon Sep 17 00:00:00 2001 From: Haoran Un Date: Tue, 22 Oct 2024 14:02:57 +1100 Subject: [PATCH 2/3] Add PDFPage.drawLink() to add a URL to PDFs. See https://github.com/Hopding/pdf-lib/issues/555#issuecomment-670080225 --- src/api/PDFPage.ts | 84 ++++++++++++++++++++++++++++++++++++++- src/api/PDFPageOptions.ts | 9 +++++ tests/api/PDFPage.spec.ts | 23 ++++++++++- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/api/PDFPage.ts b/src/api/PDFPage.ts index 7333440bd..14c96dcee 100644 --- a/src/api/PDFPage.ts +++ b/src/api/PDFPage.ts @@ -1,4 +1,8 @@ -import { Color, rgb } from 'src/api/colors'; +import { + Color, + colorToComponents, + rgb +} from 'src/api/colors'; import { drawImage, drawLine, @@ -24,6 +28,7 @@ import { PDFPageDrawEllipseOptions, PDFPageDrawImageOptions, PDFPageDrawLineOptions, + PDFPageDrawLinkOptions, PDFPageDrawPageOptions, PDFPageDrawRectangleOptions, PDFPageDrawSquareOptions, @@ -42,6 +47,7 @@ import { PDFRef, PDFDict, PDFArray, + PDFString, } from 'src/core'; import { assertEachIs, @@ -1325,6 +1331,82 @@ export default class PDFPage { ); } + +/** + * Draw a link on this page. + * + * In the PDF specification,you define a rectangular section of the page + * that when clicked should activate the link + * + * For example: + * ```js + * page.drawLink('https://pdf-lib.js.org', { + * x: 50, + * y: 50, + * width: 200, + * height: 100 + * }) + * ``` + * @param options The options to be used when drawing the line. + * @see https://github.com/Hopding/pdf-lib/issues/555#issuecomment-670195238 + */ + drawLink(link: string, options: PDFPageDrawLinkOptions): void { + assertIs(options.x, 'options.x', ['number']); + assertIs(options.y, 'options.y', ['number']); + assertIs(options.width, 'options.width', ['number']); + assertIs(options.height, 'options.height', ['number']); + + assertOrUndefined(options.borderWidth, 'options.borderWidth', ['number']); + assertOrUndefined(options.color, 'options.color', [ + [Object, 'Color'], + ]); + + const pdfRef: PDFRef = this.doc.context.register( + this.doc.context.obj({ + Type: 'Annot', + Subtype: 'Link', + Rect: [ + // lower left x coord + options.x, + // lower left y coord + options.y, + // upper right x coord + options.x + options.width, + // upper right y coord + options.y + options.height + ], + + // The three parameters are: + // * horizontal corner radius, + // * vertical corner radius, and + // * border width + // + // Default to a square border, 0 width. + Border: [0, 0, options.borderWidth ?? 0], + + /* Default to transparent */ + C: options.color ? colorToComponents(options.color) : [], + + // Name unique identifier + // NM: string + + /* Page to be visited when the link is clicked */ + A: { + Type: 'Action', + S: 'URI', + URI: PDFString.of(link), + }, + }) + ) + + // Annots Dictionary may or may not exist--create it if it doesn't. + if (this.node.Annots()) { + this.node.Annots()?.push(pdfRef); + } else { + this.node.set(PDFName.Annots, this.doc.context.obj([pdfRef])); + } + } + /** * Draw a rectangle on this page. For example: * ```js diff --git a/src/api/PDFPageOptions.ts b/src/api/PDFPageOptions.ts index 6ddd8357f..c35113cc0 100644 --- a/src/api/PDFPageOptions.ts +++ b/src/api/PDFPageOptions.ts @@ -88,6 +88,15 @@ export interface PDFPageDrawLineOptions { blendMode?: BlendMode; } +export interface PDFPageDrawLinkOptions { + x: number; + y: number; + width: number; + height: number; + borderWidth?: number; + color?: Color; +} + export interface PDFPageDrawRectangleOptions { x?: number; y?: number; diff --git a/tests/api/PDFPage.spec.ts b/tests/api/PDFPage.spec.ts index e02ece789..5e320945b 100644 --- a/tests/api/PDFPage.spec.ts +++ b/tests/api/PDFPage.spec.ts @@ -1,5 +1,5 @@ import fs from 'fs'; -import { PDFArray, PDFDocument, PDFName, StandardFonts } from 'src/index'; +import { PDFArray, PDFDocument, PDFName, PDFRef, StandardFonts } from 'src/index'; const birdPng = fs.readFileSync('assets/images/greyscale_bird.png'); @@ -148,4 +148,25 @@ describe(`PDFPage`, () => { expect(key1).not.toEqual(key2); expect(page2.node.normalizedEntries().Font.keys()).toEqual([key1, key2]); }); + + it(`drawLink() creates a PDFRef inside the Annots node`, async () => { + const pdfDoc = await PDFDocument.create(); + const page = pdfDoc.addPage(); + + page.drawLink( + 'https://pdf-lib.js.org', + { + x: 5, + y: 5, + width: 20, + height: 50, + } + ); + + const annots = page.node.normalizedEntries().Annots; + expect(annots).toBeInstanceOf(PDFArray); + expect(annots.size()).toEqual(1); + const pdfRef = annots.asArray().pop(); + expect(pdfRef).toBeInstanceOf(PDFRef); + }); }); From af70e9caef7056ab6d016b196490e2827309ee91 Mon Sep 17 00:00:00 2001 From: Haoran Un Date: Tue, 22 Oct 2024 14:19:02 +1100 Subject: [PATCH 3/3] Lint. --- src/api/PDFPage.ts | 27 ++++++++++----------------- tests/api/PDFPage.spec.ts | 23 +++++++++++++---------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/api/PDFPage.ts b/src/api/PDFPage.ts index 14c96dcee..d7708724f 100644 --- a/src/api/PDFPage.ts +++ b/src/api/PDFPage.ts @@ -1,8 +1,4 @@ -import { - Color, - colorToComponents, - rgb -} from 'src/api/colors'; +import { Color, colorToComponents, rgb } from 'src/api/colors'; import { drawImage, drawLine, @@ -1331,10 +1327,9 @@ export default class PDFPage { ); } - -/** + /** * Draw a link on this page. - * + * * In the PDF specification,you define a rectangular section of the page * that when clicked should activate the link * @@ -1357,9 +1352,7 @@ export default class PDFPage { assertIs(options.height, 'options.height', ['number']); assertOrUndefined(options.borderWidth, 'options.borderWidth', ['number']); - assertOrUndefined(options.color, 'options.color', [ - [Object, 'Color'], - ]); + assertOrUndefined(options.color, 'options.color', [[Object, 'Color']]); const pdfRef: PDFRef = this.doc.context.register( this.doc.context.obj({ @@ -1373,7 +1366,7 @@ export default class PDFPage { // upper right x coord options.x + options.width, // upper right y coord - options.y + options.height + options.y + options.height, ], // The three parameters are: @@ -1387,8 +1380,8 @@ export default class PDFPage { /* Default to transparent */ C: options.color ? colorToComponents(options.color) : [], - // Name unique identifier - // NM: string + // Name unique identifier + // NM: string /* Page to be visited when the link is clicked */ A: { @@ -1396,9 +1389,9 @@ export default class PDFPage { S: 'URI', URI: PDFString.of(link), }, - }) - ) - + }), + ); + // Annots Dictionary may or may not exist--create it if it doesn't. if (this.node.Annots()) { this.node.Annots()?.push(pdfRef); diff --git a/tests/api/PDFPage.spec.ts b/tests/api/PDFPage.spec.ts index 5e320945b..e386ac7aa 100644 --- a/tests/api/PDFPage.spec.ts +++ b/tests/api/PDFPage.spec.ts @@ -1,5 +1,11 @@ import fs from 'fs'; -import { PDFArray, PDFDocument, PDFName, PDFRef, StandardFonts } from 'src/index'; +import { + PDFArray, + PDFDocument, + PDFName, + PDFRef, + StandardFonts, +} from 'src/index'; const birdPng = fs.readFileSync('assets/images/greyscale_bird.png'); @@ -153,15 +159,12 @@ describe(`PDFPage`, () => { const pdfDoc = await PDFDocument.create(); const page = pdfDoc.addPage(); - page.drawLink( - 'https://pdf-lib.js.org', - { - x: 5, - y: 5, - width: 20, - height: 50, - } - ); + page.drawLink('https://pdf-lib.js.org', { + x: 5, + y: 5, + width: 20, + height: 50, + }); const annots = page.node.normalizedEntries().Annots; expect(annots).toBeInstanceOf(PDFArray);