For a current project (document management), we were given a seemingly simple task:
Implement a PDF preview where I can see all full-text search results highlighted.
Searching for a single value is easy: just press Ctrl + F
, search for the term, and it gets highlighted automatically.
However, highlighting multiple values is a bit trickier. After some research, we discovered that only one major PDF viewer supports this functionality out of the box: Mozilla Firefox’s PDF.js viewer.
Implementing the viewer
We integrated the viewer as follows:
<iframe id="@_pdfPreviewId" title="document" type="application/pdf" src="/web/viewer.html?file=" class="pdf-preview"></iframe>
This loads PDF.js with an empty view. We found that the required functionality is only accessible through JavaScript. Note that the ID changes dynamically with the currently previewed document to prevent the iframe from displaying the wrong content.
Whenever the displayed document changes, the previous preview immediately disappears.
/// <summary> Dynamic id coupled to the preview id to prevent race condition while loading pdfs </summary>
private string _pdfPreviewId => "pdf-preview_" + _previewedDocument.Id;
Whenever the displayed document changes the previous preview immediately disappears.
Implementing the logic
Next, we call JavaScript from our C# Blazor code, passing in the necessary parameters.
await JS.InvokeVoidAsync("documentPreview.open", _pdfPreviewId, _previewedDocument.DownloadPath, valuesToHighlight);
This is the JavaScript code we used:
var documentPreview = {
async open(iframeId, url, valuesToHighlight) {
let iframe = document.getElementById(iframeId);
// Wait until available
let counter = 0;
while (!iframe || !iframe.contentWindow || !iframe.contentWindow.PDFViewerApplication) {
counter++;
// Wait for a maximum of 10 seconds
// This will happen if the PDF preview is getting replaced (by iframe id) before the file loading has started
if (counter >= 100) {
return;
}
await new Promise(resolve => setTimeout(resolve, 100));
// Prevent a bug where there are multiple iframes with the same id getting created and replaced
// This would lead to the iframe displaying the wrong file
iframe = document.getElementById(iframeId);
}
// Open the PDF with credentials (we're using msal; without this we get 401 Unauthorized)
try {
await iframe.contentWindow.PDFViewerApplication.open({
url: url,
withCredentials: true
});
} catch (ex) {
console.log("Failed to open PDF: " + ex);
}
// Show highlights
if (valuesToHighlight?.length > 0) {
console.log("valuesToHighlight: " + valuesToHighlight);
var eventBus = iframe.contentWindow.PDFViewerApplication.pdfViewer.eventBus;
var searchOption = {
query: valuesToHighlight,
caseSensitive: false,
highlightAll: true,
phraseSearch: true
};
eventBus.dispatch("find", searchOption)
}
}
}
The result
And there it is: beautiful multi-term highlighting in action.
One thing to keep in mind: using the search feature within the viewer will break the highlighting.
Tidak ada komentar:
Posting Komentar