diff --git a/packages/render/src/index.js b/packages/render/src/index.js index 0701c7f73..534a17e4c 100644 --- a/packages/render/src/index.js +++ b/packages/render/src/index.js @@ -1,13 +1,16 @@ import renderNode from './primitives/renderNode'; -import addBookmarks from './operations/addBookmarks'; +import addNodeBookmark from './operations/addNodeBookmark'; const render = (ctx, doc) => { const pages = doc.children || []; const options = { imageCache: new Map() }; - pages.forEach((page) => renderNode(ctx, page, options)); + const registry = {}; - addBookmarks(ctx, doc); + pages.forEach((page, index) => { + renderNode(ctx, page, options); + addNodeBookmark(ctx, page, index, registry); + }); ctx.end(); diff --git a/packages/render/src/operations/addBookmarks.js b/packages/render/src/operations/addNodeBookmark.js similarity index 74% rename from packages/render/src/operations/addBookmarks.js rename to packages/render/src/operations/addNodeBookmark.js index 00ddabc14..156170198 100644 --- a/packages/render/src/operations/addBookmarks.js +++ b/packages/render/src/operations/addNodeBookmark.js @@ -14,7 +14,6 @@ const addNodeBookmark = (ctx, node, pageNumber, registry) => { zoom, fit, }); - registry[bookmark.ref] = instance; } @@ -25,14 +24,4 @@ const addNodeBookmark = (ctx, node, pageNumber, registry) => { ); }; -const addBookmarks = (ctx, root) => { - const registry = {}; - - const pages = root.children || []; - - pages.forEach((page, i) => { - addNodeBookmark(ctx, page, i, registry); - }); -}; - -export default addBookmarks; +export default addNodeBookmark; diff --git a/packages/render/tests/operations/bookmarks.test.js b/packages/render/tests/operations/bookmarks.test.js new file mode 100644 index 000000000..54a5ddf58 --- /dev/null +++ b/packages/render/tests/operations/bookmarks.test.js @@ -0,0 +1,73 @@ +import { describe, expect, test } from 'vitest'; + +import * as P from '@react-pdf/primitives'; + +import PDFDocument from '../../../pdfkit/lib/pdfkit'; +import render from '../../src/index'; + +/** + * Original issue for this test was that all bookmarks incorrectly + * pointed to the last page rather than the page for which they were created. + */ +describe('operations bookmarks', () => { + test('should create 3 pages with unique bookmarks', () => { + const ctx = new PDFDocument({ autoFirstPage: false }); + const BOOKMARK1 = 'Bookmark 1'; + const BOOKMARK2 = 'Bookmark 2'; + const BOOKMARK3 = 'Bookmark 3'; + const p1 = { bookmark: { ref: 1, title: BOOKMARK1 } }; + const p2 = { bookmark: { ref: 2, title: BOOKMARK2 } }; + const p3 = { bookmark: { ref: 3, title: BOOKMARK3 } }; + const box = { left: 0, top: 0, width: 100, height: 100, x: 0, y: 0 }; + const makeLine = (pageNumber) => { + const line = { + ascent: 16.2, + box, + descent: 0, + height: 100, + overflowLeft: 0, + overflowRight: 0, + runs: [], + string: `Page ${pageNumber}`, + decorationLines: [], + }; + return line; + }; + const doc = { + type: P.Document, + children: [ + { + children: [{ lines: [makeLine(1)], box, props: p1, type: P.Text }], + box, + type: P.Page, + }, + { + children: [{ lines: [makeLine(2)], box, props: p2, type: P.Text }], + box, + type: P.Page, + }, + { + children: [{ lines: [makeLine(3)], box, props: p3, type: P.Text }], + box, + type: P.Page, + }, + ], + }; + + render(ctx, doc); + + const kids = ctx._root.data.Pages.data.Kids; + expect(kids).toHaveLength(3); + // We expect unique ids + expect( + kids[0].id !== kids[1].id && + kids[0].id !== kids[2].id && + kids[1].id !== kids[2].id, + ).toBe(true); + const children = ctx._root.document.outline.children; + expect(children).toHaveLength(3); + expect(children[0].outlineData.Title.toString()).toBe(BOOKMARK1); + expect(children[1].outlineData.Title.toString()).toBe(BOOKMARK2); + expect(children[2].outlineData.Title.toString()).toBe(BOOKMARK3); + }); +});