Skip to content

Commit 0f6c337

Browse files
feat: Implement PDF viewer with search, rendering, and outline functionality using pdfjs-dist.
1 parent 8390ff4 commit 0f6c337

File tree

1 file changed

+90
-89
lines changed

1 file changed

+90
-89
lines changed

viewer/js/index.js

Lines changed: 90 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -435,107 +435,108 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger = 0) {
435435
};
436436

437437
task.promise.catch(handleRenderingError);
438+
}
438439

439-
globalThis.onRenderPage = function (zoom) {
440-
if (pageRendering) {
441-
if (newPageNumber === channel.getPage() && newZoomRatio === channel.getZoomRatio() &&
442-
orientationDegrees === channel.getDocumentOrientationDegrees()) {
443-
useRender = true;
444-
return;
445-
}
440+
globalThis.onRenderPage = function (zoom) {
441+
if (pageRendering) {
442+
if (newPageNumber === channel.getPage() && newZoomRatio === channel.getZoomRatio() &&
443+
orientationDegrees === channel.getDocumentOrientationDegrees()) {
444+
useRender = true;
445+
return;
446+
}
446447

447-
renderPending = true;
448-
renderPendingZoom = zoom;
449-
if (task !== null) {
450-
task.cancel();
451-
task = null;
452-
}
453-
} else {
454-
renderPage(channel.getPage(), zoom, false);
448+
renderPending = true;
449+
renderPendingZoom = zoom;
450+
if (task !== null) {
451+
task.cancel();
452+
task = null;
455453
}
456-
};
454+
} else {
455+
renderPage(channel.getPage(), zoom, false);
456+
}
457+
};
457458

458-
globalThis.isTextSelected = function () {
459-
return globalThis.getSelection().toString() !== "";
460-
};
459+
globalThis.isTextSelected = function () {
460+
return globalThis.getSelection().toString() !== "";
461+
};
461462

462-
globalThis.getDocumentOutline = function () {
463-
pdfDoc.getOutline().then(function (outline) {
464-
getSimplifiedOutline(outline, outlineAbort).then(function (outlineEntries) {
465-
if (outlineEntries !== null) {
466-
channel.setDocumentOutline(JSON.stringify(outlineEntries));
467-
} else {
468-
channel.setDocumentOutline(null);
469-
}
470-
}).catch(function (error) {
471-
console.log("getSimplifiedOutline error: " + error);
472-
});
463+
globalThis.getDocumentOutline = function () {
464+
pdfDoc.getOutline().then(function (outline) {
465+
getSimplifiedOutline(outline, outlineAbort).then(function (outlineEntries) {
466+
if (outlineEntries !== null) {
467+
channel.setDocumentOutline(JSON.stringify(outlineEntries));
468+
} else {
469+
channel.setDocumentOutline(null);
470+
}
473471
}).catch(function (error) {
474-
console.log("pdfDoc.getOutline error: " + error);
472+
console.log("getSimplifiedOutline error: " + error);
475473
});
476-
};
474+
}).catch(function (error) {
475+
console.log("pdfDoc.getOutline error: " + error);
476+
});
477+
};
477478

478-
globalThis.abortDocumentOutline = function () {
479-
outlineAbort.abort();
480-
outlineAbort = new AbortController();
481-
};
479+
globalThis.abortDocumentOutline = function () {
480+
outlineAbort.abort();
481+
outlineAbort = new AbortController();
482+
};
483+
484+
globalThis.toggleTextLayerVisibility = function () {
485+
let textLayerForeground = "red";
486+
if (isTextLayerVisible) {
487+
textLayerForeground = "transparent";
488+
}
489+
document.documentElement.style.setProperty("--text-layer-foreground", textLayerForeground);
490+
isTextLayerVisible = !isTextLayerVisible;
491+
};
482492

483-
globalThis.toggleTextLayerVisibility = function () {
484-
let textLayerForeground = "red";
485-
if (isTextLayerVisible) {
486-
textLayerForeground = "transparent";
493+
globalThis.loadDocument = function () {
494+
const pdfPassword = channel.getPassword();
495+
const loadingTask = getDocument({
496+
url: "https://localhost/placeholder.pdf",
497+
cMapUrl: "https://localhost/viewer/cmaps/",
498+
cMapPacked: true,
499+
password: pdfPassword,
500+
iccUrl: "https://localhost/viewer/iccs/",
501+
isEvalSupported: false,
502+
// If a font isn't embedded, the viewer falls back to default system fonts. On Android,
503+
// there often isn't a good substitution provided by the OS, so we need to bundle standard
504+
// fonts to improve the rendering of certain PDFs:
505+
//
506+
// https://github.com/mozilla/pdf.js/pull/18465
507+
// https://bugzilla.mozilla.org/show_bug.cgi?id=1882613
508+
useSystemFonts: false,
509+
standardFontDataUrl: "https://localhost/viewer/standard_fonts/",
510+
wasmUrl: "https://localhost/viewer/wasm/"
511+
});
512+
loadingTask.onPassword = (_, error) => {
513+
if (error === PasswordResponses.NEED_PASSWORD) {
514+
channel.showPasswordPrompt();
515+
} else if (error === PasswordResponses.INCORRECT_PASSWORD) {
516+
channel.invalidPassword();
487517
}
488-
document.documentElement.style.setProperty("--text-layer-foreground", textLayerForeground);
489-
isTextLayerVisible = !isTextLayerVisible;
490518
};
491519

492-
globalThis.loadDocument = function () {
493-
const pdfPassword = channel.getPassword();
494-
const loadingTask = getDocument({
495-
url: "https://localhost/placeholder.pdf",
496-
cMapUrl: "https://localhost/viewer/cmaps/",
497-
cMapPacked: true,
498-
password: pdfPassword,
499-
iccUrl: "https://localhost/viewer/iccs/",
500-
isEvalSupported: false,
501-
// If a font isn't embedded, the viewer falls back to default system fonts. On Android,
502-
// there often isn't a good substitution provided by the OS, so we need to bundle standard
503-
// fonts to improve the rendering of certain PDFs:
504-
//
505-
// https://github.com/mozilla/pdf.js/pull/18465
506-
// https://bugzilla.mozilla.org/show_bug.cgi?id=1882613
507-
useSystemFonts: false,
508-
standardFontDataUrl: "https://localhost/viewer/standard_fonts/",
509-
wasmUrl: "https://localhost/viewer/wasm/"
520+
loadingTask.promise.then(function (newDoc) {
521+
channel.onLoaded();
522+
pdfDoc = newDoc;
523+
channel.setNumPages(pdfDoc.numPages);
524+
pdfDoc.getMetadata().then(function (data) {
525+
channel.setDocumentProperties(JSON.stringify(data.info));
526+
}).catch(function (error) {
527+
console.log("getMetadata error: " + error);
510528
});
511-
loadingTask.onPassword = (_, error) => {
512-
if (error === PasswordResponses.NEED_PASSWORD) {
513-
channel.showPasswordPrompt();
514-
} else if (error === PasswordResponses.INCORRECT_PASSWORD) {
515-
channel.invalidPassword();
516-
}
517-
};
518-
519-
loadingTask.promise.then(function (newDoc) {
520-
channel.onLoaded();
521-
pdfDoc = newDoc;
522-
channel.setNumPages(pdfDoc.numPages);
523-
pdfDoc.getMetadata().then(function (data) {
524-
channel.setDocumentProperties(JSON.stringify(data.info));
525-
}).catch(function (error) {
526-
console.log("getMetadata error: " + error);
527-
});
528-
pdfDoc.getOutline().then(function (outline) {
529-
channel.setHasDocumentOutline(outline && outline.length > 0);
530-
}).catch(function (error) {
531-
console.log("getOutline error: " + error);
532-
});
533-
renderPage(channel.getPage(), false, false);
534-
}, function (reason) {
535-
console.error(reason.name + ": " + reason.message);
529+
pdfDoc.getOutline().then(function (outline) {
530+
channel.setHasDocumentOutline(outline && outline.length > 0);
531+
}).catch(function (error) {
532+
console.log("getOutline error: " + error);
536533
});
537-
};
534+
renderPage(channel.getPage(), false, false);
535+
}, function (reason) {
536+
console.error(reason.name + ": " + reason.message);
537+
});
538+
};
538539

539-
globalThis.onresize = () => {
540-
setLayerTransform(canvas.clientWidth, canvas.clientHeight, textLayerDiv);
541-
};
540+
globalThis.onresize = () => {
541+
setLayerTransform(canvas.clientWidth, canvas.clientHeight, textLayerDiv);
542+
};

0 commit comments

Comments
 (0)