PDF.js で特定の文字列を含むPDF要素をハイライトする方法のメモ。
ここでは、PDF.js と PDF描画用の html ファイルは以下のようなディレクトリに配置しています。
PDF から getTextContent() で PDF のテキスト要素を取得して、
テキストが特定の条件を満たす場合(ここでは JavaScript を含む場合)に
ハイライトするようにします。
ハイライトは、PDFのテキスト要素と同じ位置・サイズに半透明の div タグを描画します。
位置、サイズは以下のように設定します。
また、ハイライト用の div タグを hl_parent に追加しておき、ページ遷移した際には前のページの div タグが残らないように、
hl_parent 内の div タグを削除します。
全ソースは以下の通り。
ここでは、PDF.js と PDF描画用の html ファイルは以下のようなディレクトリに配置しています。
www ├── pdfjs # pdf.js │ ├── build │ ├── LICENSE │ └── web │ └── compressed.tracemonkey-pldi-09.pdf # このPDFを描画 └── test └── test_viewer.html # PDF描画用の html ファイル
PDF から getTextContent() で PDF のテキスト要素を取得して、
テキストが特定の条件を満たす場合(ここでは JavaScript を含む場合)に
ハイライトするようにします。
page.getTextContent().then(function (textContent) { ... textContent.items.forEach(function (item) { if (item['str'].match(/JavaScript/)) { ... } }); );
ハイライトは、PDFのテキスト要素と同じ位置・サイズに半透明の div タグを描画します。
位置、サイズは以下のように設定します。
var item_left = item['transform'][4]; var item_top = item['transform'][5]; var rect = [item_left, item_top, item_left + item['width'], item_top + item['height']]; var view_rect = viewport.convertToViewportRectangle(rect); var abs_width = Math.abs(view_rect[0] - view_rect[2]); var abs_height = Math.abs(view_rect[1] - view_rect[3]); var abs_left = canvas_left + Math.min(view_rect[0], view_rect[2]); var abs_top = canvas_top + Math.min(view_rect[1], view_rect[3]);
また、ハイライト用の div タグを hl_parent に追加しておき、ページ遷移した際には前のページの div タグが残らないように、
hl_parent 内の div タグを削除します。
while (hl_parent.firstChild) { hl_parent.removeChild(hl_parent.firstChild); }
全ソースは以下の通り。
<html>
<head>
<title>highlight</title>
</head>
<body>
highlight
<canvas id="the-canvas" style="border: solid;1px;"></canvas>
<button id="prev">Previous</button>
<button id="next">Next</button>
Page: /
<script src="/www/pdfjs/build/pdf.js"></script>
<script type="text/javascript">
var url = '/www/pdfjs/web/compressed.tracemonkey-pldi-09.pdf';
var pdfjsLib = window['pdfjs-dist/build/pdf'];
pdfjsLib.GlobalWorkerOptions.workerSrc = '/www/pdfjs/build/pdf.worker.js';
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 1.2,
canvas = document.getElementById('the-canvas'),
ctx = canvas.getContext('2d');
// ハイライト表示用のタグ
var hl_parent = document.getElementById('highlight_parent');
// canvas の位置
var canvas_rect = canvas.getBoundingClientRect();
// ページ内の特定テキストをハイライト
function highlightTexts(page) { while (hl_parent.firstChild) { hl_parent.removeChild(hl_parent.firstChild); }
page.getTextContent().then(function (textContent) {
var pageElement = canvas.parentElement;
var viewport = page.getViewport({'scale': scale});
// highlight
textContent.items.forEach(function (item) {
if (item['str'].match(/JavaScript/)) { var item_left = item['transform'][4]; var item_top = item['transform'][5]; var rect = [item_left, item_top, item_left + item['width'], item_top + item['height']]; var view_rect = viewport.convertToViewportRectangle(rect); var abs_width = Math.abs(view_rect[0] - view_rect[2]); var abs_height = Math.abs(view_rect[1] - view_rect[3]); var abs_left = canvas_left + Math.min(view_rect[0], view_rect[2]); var abs_top = canvas_top + Math.min(view_rect[1], view_rect[3]);
var style = 'position: absolute;'
+ ' opacity: 0.50;'
+ ' background-color: yellow;'
+ ' left:' + String(abs_left) + 'px;'
+ ' top:' + String(abs_top) + 'px;'
+ ' width:' + String(abs_width) + 'px;'
+ ' height: ' + String(abs_height) + 'px;';
var e = document.createElement('div');
e.setAttribute('style', style);
hl_parent.appendChild(e);
}
});
});
}
function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
// Wait for rendering to finish
renderTask.promise.then(function() {
// highlight
highlightTexts(page);
pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
document.getElementById('page_num').textContent = num;
}
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('prev').addEventListener('click', onPrevPage);
function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('next').addEventListener('click', onNextPage);
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('page_count').textContent = pdfDoc.numPages;
// Initial/first page rendering
renderPage(pageNum);
});
</script>
</body>
</html>