Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

# Project-generated directories and files
__screenshots__
.vitest-attachments

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relevant? Why was this needed now?

coverage
dist
node_modules
Expand Down
2 changes: 1 addition & 1 deletion packages/react-pdf/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"make-cancellable-promise": "^2.0.0",
"make-event-props": "^2.0.0",
"merge-refs": "^2.0.0",
"pdfjs-dist": "5.4.296",
"pdfjs-dist": "6.0.227",
"tiny-invariant": "^1.0.0",
"warning": "^4.0.0"
},
Expand Down
16 changes: 3 additions & 13 deletions packages/react-pdf/src/Document.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { beforeAll, describe, expect, it, vi } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { page, userEvent } from 'vitest/browser';
import { render } from 'vitest-browser-react';
import { createRef } from 'react';

import Document from './Document.js';
import DocumentContext from './DocumentContext.js';
import { pdfjs } from './index.test.js';
import Page from './Page.js';

import { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../test-utils.js';

import type { PDFDocumentProxy } from 'pdfjs-dist';
import type LinkService from './LinkService.js';
import type { ScrollPageIntoViewArgs } from './shared/types.js';

Expand Down Expand Up @@ -49,16 +47,8 @@ async function waitForAsync() {

describe('Document', () => {
// Object with basic loaded PDF information that shall match after successful loading
const desiredLoadedPdf: Partial<PDFDocumentProxy> = {};
const desiredLoadedPdf2: Partial<PDFDocumentProxy> = {};

beforeAll(async () => {
const pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;
desiredLoadedPdf._pdfInfo = pdf._pdfInfo;

const pdf2 = await pdfjs.getDocument({ data: pdfFile2.arrayBuffer }).promise;
desiredLoadedPdf2._pdfInfo = pdf2._pdfInfo;
});
const desiredLoadedPdf = { numPages: 4 };
const desiredLoadedPdf2 = { numPages: 5 };

describe('loading', () => {
it('loads a file and calls onSourceSuccess and onLoadSuccess callbacks via data URI properly', async () => {
Expand Down
7 changes: 3 additions & 4 deletions packages/react-pdf/src/LinkService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import invariant from 'tiny-invariant';

import type { PDFDocumentProxy } from 'pdfjs-dist';
import type { IPDFLinkService } from 'pdfjs-dist/types/web/interfaces.js';
import type {
Dest,
ExternalLinkRel,
Expand All @@ -31,7 +30,7 @@ type PDFViewer = {
scrollPageIntoView: (args: ScrollPageIntoViewArgs) => void;
};

export default class LinkService implements IPDFLinkService {
export default class LinkService {
externalLinkEnabled: boolean;
externalLinkRel?: ExternalLinkRel;
externalLinkTarget?: ExternalLinkTarget;
Expand Down Expand Up @@ -192,8 +191,8 @@ export default class LinkService implements IPDFLinkService {
// Intentionally empty
}

executeSetOCGState(): void {
// Intentionally empty
executeSetOCGState(): Promise<void> {
return Promise.resolve();
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or:

Suggested change
executeSetOCGState(): Promise<void> {
return Promise.resolve();
}
async executeSetOCGState(): Promise<void> {
// Intentionally empty
}


isPageVisible(): boolean {
Expand Down
27 changes: 6 additions & 21 deletions packages/react-pdf/src/Page.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import silentlyFailingPdf from '../../../__mocks__/_silently_failing_pdf.js';

import { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../test-utils.js';

import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';
import type { PDFDocumentProxy } from 'pdfjs-dist';
import type { DocumentContextType, PageCallback } from './shared/types.js';

const pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');
Expand Down Expand Up @@ -58,34 +58,19 @@ describe('Page', () => {
let pdf5: PDFDocumentProxy;

// Object with basic loaded page information that shall match after successful loading
const desiredLoadedPage: Partial<PDFPageProxy> = {};
const desiredLoadedPage2: Partial<PDFPageProxy> = {};
const desiredLoadedPage3: Partial<PDFPageProxy> = {};
const desiredLoadedPage = { pageNumber: 1 };
const desiredLoadedPage2 = { pageNumber: 2 };
const desiredLoadedPage3 = { pageNumber: 1 };

// Callbacks used in registerPage and unregisterPage callbacks
let registerPageArguments: [number, HTMLDivElement];
let unregisterPageArguments: [number];
const registerPageArguments: [number, HTMLDivElement] = [0, expect.any(HTMLDivElement)];
const unregisterPageArguments: [number] = [0];

beforeAll(async () => {
pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;

const page = await pdf.getPage(1);
desiredLoadedPage._pageIndex = page._pageIndex;
desiredLoadedPage._pageInfo = page._pageInfo;

const page2 = await pdf.getPage(2);
desiredLoadedPage2._pageIndex = page2._pageIndex;
desiredLoadedPage2._pageInfo = page2._pageInfo;

pdf2 = await pdfjs.getDocument({ data: pdfFile2.arrayBuffer }).promise;

const page3 = await pdf2.getPage(1);
desiredLoadedPage3._pageIndex = page3._pageIndex;
desiredLoadedPage3._pageInfo = page3._pageInfo;

registerPageArguments = [page._pageIndex, expect.any(HTMLDivElement)];
unregisterPageArguments = [page._pageIndex];

pdf4 = await pdfjs.getDocument({ data: pdfFile4.arrayBuffer }).promise;

pdf5 = await pdfjs.getDocument({ data: pdfFile5.arrayBuffer }).promise;
Expand Down
33 changes: 24 additions & 9 deletions packages/react-pdf/src/Page/AnnotationLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import useResolver from '../shared/hooks/useResolver.js';
import { cancelRunningTask } from '../shared/utils.js';

import type { AnnotationLayerParameters } from 'pdfjs-dist/types/src/display/annotation_layer.js';
import type { PDFLinkService } from 'pdfjs-dist/types/web/pdf_link_service.js';
import type { Annotations } from '../shared/types.js';

export default function AnnotationLayer(): React.ReactElement {
Expand Down Expand Up @@ -180,25 +181,39 @@ export default function AnnotationLayer(): React.ReactElement {
annotationStorage: pdf.annotationStorage,
div: layer,
imageResourcesPath,
linkService,
linkService: linkService as unknown as PDFLinkService,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of type assertions, let's fix that in a way that does not involve lying to TypeScript

page,
renderForms,
viewport: clonedViewport,
};

layer.innerHTML = '';

try {
new pdfjs.AnnotationLayer(annotationLayerParameters).render(renderParameters);
let cancelled = false;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makeCancellable should handle that by default without the need of manually checking if promise has been cancelled.


// Intentional immediate callback
onRenderSuccess();
} catch (error) {
onRenderError(error);
}
const cancellable = makeCancellable(
new pdfjs.AnnotationLayer({
...annotationLayerParameters,
linkService: linkService as unknown as PDFLinkService,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I would rather not lie to TypeScript here.

}).render(renderParameters),
);
const runningTask = cancellable;

cancellable.promise
.then(() => {
if (!cancelled) {
onRenderSuccess();
}
})
.catch((error) => {
if (!cancelled) {
onRenderError(error);
}
});

return () => {
// TODO: Cancel running task?
cancelled = true;
cancelRunningTask(runningTask);
};
},
[
Expand Down
16 changes: 13 additions & 3 deletions packages/react-pdf/src/Page/TextLayer.spec.tsx

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand these changes yet...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PDF.js 6 can render more [role="presentation"] nodes than textContent.items.length when marked content is included. Added a getRenderedTextItemCount() helper with a comment explaining this, instead of comparing against desiredTextItems.length.

Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ describe('TextLayer', () => {
// Loaded page text items
let desiredTextItems: TextContent['items'];
let desiredTextItems2: TextContent['items'];
let desiredRenderedTextItemCount: number;

beforeAll(async () => {
const pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;
Expand All @@ -57,6 +58,15 @@ describe('TextLayer', () => {
const textContent = await page.getTextContent();
desiredTextItems = textContent.items;

const layer = document.createElement('div');
const textLayer = new pdfjs.TextLayer({
container: layer,
textContentSource: page.streamTextContent({ includeMarkedContent: true }),
viewport: page.getViewport({ scale: 1 }),
});
await textLayer.render();
desiredRenderedTextItemCount = layer.querySelectorAll('[role="presentation"]').length;

page2 = await pdf.getPage(2);
const textContent2 = await page2.getTextContent();
desiredTextItems2 = textContent2.items;
Expand Down Expand Up @@ -150,7 +160,7 @@ describe('TextLayer', () => {

const textItems = getTextItems(container);

expect(textItems).toHaveLength(desiredTextItems.length);
expect(textItems).toHaveLength(desiredRenderedTextItemCount);
});

it('renders text content properly given customTextRenderer', async () => {
Expand All @@ -171,7 +181,7 @@ describe('TextLayer', () => {

const textItems = getTextItems(container);

expect(textItems).toHaveLength(desiredTextItems.length);
expect(textItems).toHaveLength(desiredRenderedTextItemCount);
});

it('maps textContent items to actual TextLayer children properly', async () => {
Expand Down Expand Up @@ -225,7 +235,7 @@ describe('TextLayer', () => {

const textItems = getTextItems(container);

expect(textItems).toHaveLength(desiredTextItems.length);
expect(textItems).toHaveLength(desiredRenderedTextItemCount);

expect(customTextRenderer).toHaveBeenCalledTimes(desiredTextItems.length);
expect(customTextRenderer).toHaveBeenCalledWith(
Expand Down
20 changes: 4 additions & 16 deletions packages/react-pdf/src/Thumbnail.spec.tsx

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand these changes yet...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched from asserting PDF.js internal _page* fields to public callback fields (pageNumber), which is what onLoadSuccess actually exposes. Updated the PR with a comment in the spec explaining this.

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import silentlyFailingPdf from '../../../__mocks__/_silently_failing_pdf.js';

import { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../test-utils.js';

import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';
import type { PDFDocumentProxy } from 'pdfjs-dist';
import type { DocumentContextType, PageCallback } from './shared/types.js';

const pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');
Expand Down Expand Up @@ -52,28 +52,16 @@ describe('Thumbnail', () => {
let pdf2: PDFDocumentProxy;

// Object with basic loaded page information that shall match after successful loading
const desiredLoadedThumbnail: Partial<PDFPageProxy> = {};
const desiredLoadedThumbnail2: Partial<PDFPageProxy> = {};
const desiredLoadedThumbnail3: Partial<PDFPageProxy> = {};
const desiredLoadedThumbnail = { pageNumber: 1 };
const desiredLoadedThumbnail2 = { pageNumber: 2 };
const desiredLoadedThumbnail3 = { pageNumber: 1 };

const linkService = new LinkService();

beforeAll(async () => {
pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;

const page = await pdf.getPage(1);
desiredLoadedThumbnail._pageIndex = page._pageIndex;
desiredLoadedThumbnail._pageInfo = page._pageInfo;

const page2 = await pdf.getPage(2);
desiredLoadedThumbnail2._pageIndex = page2._pageIndex;
desiredLoadedThumbnail2._pageInfo = page2._pageInfo;

pdf2 = await pdfjs.getDocument({ data: pdfFile2.arrayBuffer }).promise;

const page3 = await pdf2.getPage(1);
desiredLoadedThumbnail3._pageIndex = page3._pageIndex;
desiredLoadedThumbnail3._pageInfo = page3._pageInfo;
});

describe('loading', () => {
Expand Down
9 changes: 9 additions & 0 deletions packages/react-pdf/vitest.setup.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
import * as pdfjs from 'pdfjs-dist';

import './src/pdf.worker.entry.js';

pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.mjs',
import.meta.url,
).toString();

document.body.style.setProperty('--react-pdf-annotation-layer', '1');
document.body.style.setProperty('--react-pdf-text-layer', '1');
Loading