Встраивание PDF в браузер: полное руководство по использованию pdf.js
Для кого эта статья:
- Веб-разработчики и программисты, занимающиеся интеграцией PDF-документов в веб-приложения
- Специалисты, работающие с UX/UI дизайном и стремящиеся улучшить пользовательский опыт на своих сайтах
Студенты и начинающие разработчики, желающие освоить современные веб-технологии и библиотеки для обработки PDF
Отображение PDF-файлов прямо на веб-странице без необходимости скачивания — критически важная функция для множества проектов. От документации и учебных материалов до юридических документов и портфолио — встроенный PDF-просмотрщик существенно повышает удобство пользователей и время, проведённое на сайте. Именно здесь на сцену выходит pdf.js — мощная библиотека от Mozilla, которая перевернула способ взаимодействия с PDF в браузере. Давайте разберёмся, как правильно интегрировать её в ваш проект, избегая типичных ошибок и максимально используя все возможности этого инструмента. 🚀
Хотите быстро освоить встраивание PDF и другие современные веб-технологии? Обучение веб-разработке от Skypro — это практически ориентированная программа, где вы работаете с реальными проектами. Наши студенты учатся не только внедрять PDF-документы в веб-страницы, но и создавать полноценные интерактивные приложения под руководством практикующих разработчиков. Начните создавать профессиональные интерфейсы уже через 7 месяцев!
Что такое pdf.js и почему его стоит использовать
PDF.js — это JavaScript-библиотека с открытым исходным кодом, разработанная Mozilla для отображения PDF-документов непосредственно в браузере без использования плагинов или нативных приложений. По сути, это полноценный PDF-рендерер, написанный на чистом JavaScript, который преобразует PDF-файлы в HTML5-элементы для отображения в веб-среде.
Основная идея pdf.js заключается в том, чтобы сделать просмотр PDF максимально простым для конечного пользователя — без необходимости установки дополнительного программного обеспечения и без покидания веб-страницы. Такой подход значительно улучшает пользовательский опыт и сохраняет внимание посетителей на вашем ресурсе.
Александр Верхотуров, руководитель отдела frontend-разработки
Когда мы перепроектировали нашу систему документооборота, одной из ключевых задач было обеспечить быстрый доступ к сотням PDF-документов ежедневно. Сотрудники постоянно жаловались на необходимость скачивать каждый файл, открывать его в отдельном приложении и затем возвращаться к браузеру. Это отнимало до 40% рабочего времени.
После внедрения pdf.js мы добились просмотра документов прямо в интерфейсе системы. Более того, специалистам стало доступно параллельное сравнение нескольких документов в одном окне браузера — функция, которая была невозможна при использовании внешних просмотрщиков. Производительность команды выросла на 28%, а количество положительных отзывов об интерфейсе увеличилось в 5 раз.
Преимущества использования pdf.js для интеграции PDF в веб-страницы:
- Кроссбраузерность — работает во всех современных браузерах без дополнительных плагинов
- Безопасность — изолирует контент PDF от основного JavaScript страницы, предотвращая уязвимости
- Гибкость настройки — позволяет полностью кастомизировать интерфейс просмотрщика
- Интерактивность — поддерживает аннотации, формы и другие интерактивные элементы PDF
- Производительность — оптимизирован для быстрой загрузки и отображения даже сложных документов
- Открытый исходный код — активно поддерживается сообществом разработчиков
| Характеристика | pdf.js | Встроенный просмотрщик браузера | Сторонние плагины |
|---|---|---|---|
| Кроссбраузерная совместимость | Высокая | Ограниченная | Средняя |
| Возможность кастомизации | Полная | Отсутствует | Ограниченная |
| Необходимость установки | Нет | Нет | Да |
| JavaScript API | Богатый | Минимальный | Зависит от плагина |
| Поддержка мобильных устройств | Полная | Частичная | Ограниченная |
PDF.js существует в двух основных вариантах использования:
- PDF.js Express — готовый виджет с интерфейсом, похожим на стандартные PDF-просмотрщики
- PDF.js Core — базовый рендерер, который предоставляет только возможности для отображения PDF, но требует создания собственного интерфейса
Для большинства проектов Express-версия обеспечивает быстрый старт и привычный для пользователей интерфейс, в то время как Core-версия даёт максимальную свободу в настройке, но требует больше усилий при разработке. В нашем руководстве мы рассмотрим оба подхода, начав с более простого. 🛠️

Настройка окружения для интеграции pdf.js в веб-страницу
Прежде чем приступить к непосредственной интеграции PDF-документов, необходимо правильно настроить окружение. Процесс настройки зависит от вашего текущего стека технологий и предпочитаемого способа внедрения библиотеки.
Существует три основных способа добавить pdf.js в ваш проект:
- Через CDN — самый быстрый способ для прототипирования и простых проектов
- Через npm-пакет — оптимальный вариант для современных проектов с использованием сборщиков модулей
- Скачивание и хостинг файлов — даёт полный контроль над версией и доступностью библиотеки
Рассмотрим каждый из этих вариантов подробнее.
Пошаговое встраивание PDF в HTML с помощью pdf.js
Теперь, когда у нас есть все необходимые компоненты, приступим к пошаговой интеграции PDF-файла в HTML-страницу. Я покажу самый распространённый подход, который работает в большинстве случаев и легко адаптируется под конкретные нужды проекта. 📄
Шаг 1: Создайте базовую HTML-структуру
Начнем с создания простой HTML-страницы с контейнером для отображения PDF:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PDF Просмотрщик</title>
<style>
#pdf-container {
width: 100%;
height: 800px;
border: 1px solid #ccc;
overflow: auto;
}
</style>
</head>
<body>
<h1>Мой PDF-просмотрщик</h1>
<div id="pdf-container"></div>
<!-- Скрипты будут добавлены далее -->
</body>
</html>
Шаг 2: Подключите библиотеку pdf.js
Для простоты используем вариант через CDN, добавив перед закрывающим тегом </body> следующие скрипты:
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script>
<script>
// Устанавливаем путь к воркеру pdf.js
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.worker.min.js';
</script>
Шаг 3: Напишите JavaScript для загрузки и отображения PDF
Добавьте следующий скрипт для загрузки и отображения PDF-файла:
<script>
// Функция для загрузки и отображения PDF
function loadPDF(pdfURL) {
const container = document.getElementById('pdf-container');
// Загружаем документ
pdfjsLib.getDocument(pdfURL).promise.then(pdfDoc => {
console.log(`PDF загружен, всего страниц: ${pdfDoc.numPages}`);
// Загружаем первую страницу
return pdfDoc.getPage(1).then(page => {
// Устанавливаем масштаб
const viewport = page.getViewport({ scale: 1.5 });
// Создаем canvas для рендеринга
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
container.appendChild(canvas);
// Рендерим PDF страницу в canvas
const renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
}).catch(error => {
console.error('Ошибка при загрузке PDF:', error);
container.innerHTML = `<p>Ошибка загрузки PDF: ${error.message}</p>`;
});
}
// Вызываем функцию с URL вашего PDF-файла
loadPDF('https://путь-к-вашему-файлу.pdf');
</script>
Шаг 4: Добавьте функциональность навигации по страницам
Для полноценного просмотрщика необходимо добавить возможность переключения между страницами. Дополним нашу HTML-структуру элементами управления:
<!-- Добавьте после контейнера -->
<div class="pdf-controls">
<button id="prev-page">Предыдущая</button>
<span>Страница <span id="page-num">1</span> из <span id="page-count">?</span></span>
<button id="next-page">Следующая</button>
</div>
И расширим наш JavaScript-код для обработки переключения между страницами:
<script>
let currentPdfDoc = null;
let currentPage = 1;
let totalPages = 0;
function renderPage(pageNum) {
currentPdfDoc.getPage(pageNum).then(page => {
const container = document.getElementById('pdf-container');
const viewport = page.getViewport({ scale: 1.5 });
// Очищаем предыдущий контент
container.innerHTML = '';
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
container.appendChild(canvas);
page.render({
canvasContext: context,
viewport: viewport
});
// Обновляем номер текущей страницы
document.getElementById('page-num').textContent = pageNum;
});
}
function loadPDF(pdfURL) {
pdfjsLib.getDocument(pdfURL).promise.then(pdfDoc => {
currentPdfDoc = pdfDoc;
totalPages = pdfDoc.numPages;
document.getElementById('page-count').textContent = totalPages;
// Отображаем первую страницу
renderPage(currentPage);
// Настраиваем обработчики событий для кнопок навигации
document.getElementById('prev-page').addEventListener('click', () => {
if(currentPage > 1) {
currentPage--;
renderPage(currentPage);
}
});
document.getElementById('next-page').addEventListener('click', () => {
if(currentPage < totalPages) {
currentPage++;
renderPage(currentPage);
}
});
}).catch(error => {
console.error('Ошибка при загрузке PDF:', error);
document.getElementById('pdf-container').innerHTML =
`<p>Ошибка загрузки PDF: ${error.message}</p>`;
});
}
// Инициализация при загрузке страницы
document.addEventListener('DOMContentLoaded', () => {
loadPDF('https://путь-к-вашему-файлу.pdf');
});
</script>
Шаг 5: Обработайте загрузку PDF из разных источников
Для повышения гибкости вашего решения добавим возможность загрузки PDF как из локального источника, так и с удалённого сервера. При загрузке с удаленного сервера учтем возможные проблемы с CORS:
function loadPDF(pdfSource, isUrl = true) {
let loadingTask;
if (isUrl) {
// Загрузка из URL
loadingTask = pdfjsLib.getDocument({
url: pdfSource,
withCredentials: true // Если требуется отправка куки при кросс-доменных запросах
});
} else {
// Загрузка из ArrayBuffer (для файлов, загруженных через input)
loadingTask = pdfjsLib.getDocument({data: pdfSource});
}
loadingTask.promise.then(pdfDoc => {
// Код обработки PDF как в предыдущих шагах
}).catch(error => {
console.error('Ошибка при загрузке PDF:', error);
});
}
// Добавим обработку загрузки файлов через input
document.getElementById('pdf-file-input').addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(event) {
loadPDF(new Uint8Array(event.target.result), false);
};
reader.readAsArrayBuffer(file);
}
});
После выполнения всех этих шагов у вас будет базовый, но функциональный PDF-просмотрщик, встроенный в вашу HTML-страницу. В следующих разделах мы рассмотрим, как можно расширить и кастомизировать его для более специфических нужд. 🔧
Михаил Дронов, frontend-разработчик
На одном из проектов нам поставили задачу реализовать интерактивное пособие по сборке мебели, где пользователи должны были видеть PDF-инструкции прямо внутри приложения с возможностью масштабирования и аннотаций. Первое решение, которое мы попробовали — стандартный iframe с ссылкой на PDF, но столкнулись с массой проблем: разное отображение в браузерах, отсутствие возможности кастомизации интерфейса и главное — никакого контроля над взаимодействием пользователя с документом.
После 3 дней безуспешных экспериментов мы перешли на pdf.js, и это кардинально изменило ситуацию. В течение одного дня мы смогли не только интегрировать просмотрщик, но и добавить специальные маркеры поверх схем, интерактивные подсказки при наведении на определенные части документа и даже анимацию ключевых этапов сборки, активируемую из PDF. Конверсия завершения сборки выросла на 34%, а количество обращений в поддержку сократилось вдвое.
Кастомизация отображения PDF-документов на веб-сайте
После успешной базовой интеграции PDF-документа следующим логичным шагом является кастомизация просмотрщика под ваш дизайн и функциональные требования. В этом разделе рассмотрим различные способы настройки внешнего вида и поведения pdf.js. 🎨
Стилизация элементов просмотрщика
Начнем с базовой стилизации контейнера и элементов управления, чтобы они соответствовали общему стилю вашего сайта:
<style>
/* Контейнер для PDF */
#pdf-container {
width: 100%;
max-width: 1000px;
height: 800px;
margin: 0 auto;
border: 1px solid #e0e0e0;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: auto;
background-color: #f5f5f5;
}
/* Элементы управления */
.pdf-controls {
display: flex;
justify-content: center;
align-items: center;
padding: 10px 0;
background-color: #f8f9fa;
border-bottom: 1px solid #e0e0e0;
}
.pdf-controls button {
background-color: #4285f4;
color: white;
border: none;
padding: 8px 16px;
margin: 0 5px;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
transition: background-color 0.3s;
}
.pdf-controls button:hover {
background-color: #3367d6;
}
.pdf-controls button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.pdf-controls span {
margin: 0 15px;
font-size: 14px;
}
/* Стилизация canvas */
#pdf-container canvas {
margin: 0 auto;
display: block;
}
</style>
Добавление расширенных функций управления
Теперь расширим возможности управления просмотром, добавив масштабирование, поворот страницы и режим отображения:
<!-- Расширенные элементы управления -->
<div class="pdf-controls-extended">
<select id="zoom-select">
<option value="0.5">50%</option>
<option value="1" selected>100%</option>
<option value="1.5">150%</option>
<option value="2">200%</option>
<option value="2.5">250%</option>
</select>
<button id="rotate-cw">↻ Повернуть</button>
<select id="display-mode">
<option value="single" selected>Одна страница</option>
<option value="double">Две страницы</option>
<option value="scroll">Прокрутка</option>
</select>
<button id="download-pdf">⬇ Скачать</button>
</div>
И соответствующий JavaScript для обработки этих элементов управления:
// Глобальные настройки отображения
let currentScale = 1;
let currentRotation = 0;
let displayMode = 'single';
// Функция рендеринга с учетом всех настроек
function renderPage(pageNum) {
currentPdfDoc.getPage(pageNum).then(page => {
const container = document.getElementById('pdf-container');
container.innerHTML = '';
// Применяем текущие настройки масштаба и поворота
const viewport = page.getViewport({
scale: currentScale,
rotation: currentRotation
});
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
container.appendChild(canvas);
page.render({
canvasContext: context,
viewport: viewport
});
// Обновляем номер текущей страницы
document.getElementById('page-num').textContent = pageNum;
});
}
// Обработчики для элементов управления
document.getElementById('zoom-select').addEventListener('change', function(e) {
currentScale = parseFloat(e.target.value);
renderPage(currentPage);
});
document.getElementById('rotate-cw').addEventListener('click', function() {
// Поворачиваем по часовой стрелке на 90 градусов
currentRotation = (currentRotation + 90) % 360;
renderPage(currentPage);
});
document.getElementById('display-mode').addEventListener('change', function(e) {
displayMode = e.target.value;
if (displayMode === 'double') {
// Логика для отображения двух страниц
renderDoublePage(currentPage);
} else if (displayMode === 'scroll') {
// Логика для режима прокрутки всех страниц
renderScrollMode();
} else {
// Стандартный режим одной страницы
renderPage(currentPage);
}
});
document.getElementById('download-pdf').addEventListener('click', function() {
// Создаем ссылку для скачивания исходного PDF
const a = document.createElement('a');
a.href = pdfUrl;
a.download = 'document.pdf'; // Задайте желаемое имя файла
a.click();
});
// Функция для отображения двух страниц одновременно
function renderDoublePage(startPage) {
// Реализация отображения двух страниц
// ...
}
// Функция для отображения всего документа в режиме прокрутки
function renderScrollMode() {
// Реализация прокручиваемого режима
// ...
}
Добавление аннотаций и выделений
Для более интерактивного взаимодействия с PDF-документом, добавим возможность аннотации и выделения текста:
| Тип аннотации | Описание | Сложность реализации | Поддержка в pdf.js |
|---|---|---|---|
| Текстовые комментарии | Добавление текстовых заметок к определенным областям документа | Средняя | Полная |
| Выделение текста | Маркировка важных фрагментов текста в документе | Высокая | Частичная (требует доп. кода) |
| Рисование | Свободное рисование поверх документа | Высокая | Базовая |
| Штампы | Добавление предопределенных изображений (Одобрено, Отклонено и т.д.) | Средняя | Полная |
| Формы | Взаимодействие с формами внутри PDF | Средняя | Полная |
Реализация инструментов аннотации требует более сложного кода, но базовую версию для выделения текста можно добавить следующим образом:
// Добавление функционала выделения текста
function enableTextSelection() {
const container = document.getElementById('pdf-container');
// Добавляем слой для выделения текста
const textLayer = document.createElement('div');
textLayer.className = 'textLayer';
container.appendChild(textLayer);
currentPdfDoc.getPage(currentPage).then(page => {
page.getTextContent().then(textContent => {
// Используем встроенную функцию pdf.js для создания текстового слоя
pdfjsLib.renderTextLayer({
textContent: textContent,
container: textLayer,
viewport: page.getViewport({ scale: currentScale }),
textDivs: []
});
// После рендеринга текстового слоя добавляем обработчик выделения
setTimeout(() => {
// Добавляем обработчики для выделения текста
textLayer.addEventListener('mouseup', function() {
const selection = window.getSelection();
if (selection.toString().length > 0) {
console.log('Выделенный текст:', selection.toString());
// Здесь можно добавить логику для сохранения выделения
// или отображения панели инструментов аннотации
}
});
}, 100);
});
});
}
Интеграция с панелью закладок и миниатюрами страниц
Для улучшения навигации по большим документам добавим панель с закладками и миниатюрами страниц:
<!-- Добавьте структуру разметки -->
<div class="pdf-viewer-container">
<div class="sidebar">
<div class="tabs">
<button class="tab active" data-tab="thumbnails">Миниатюры</button>
<button class="tab" data-tab="bookmarks">Закладки</button>
</div>
<div class="tab-content active" id="thumbnails-container">
<!-- Сюда будут добавлены миниатюры -->
</div>
<div class="tab-content" id="bookmarks-container">
<!-- Сюда будут добавлены закладки -->
</div>
</div>
<div id="pdf-container"></div>
</div>
Эти примеры показывают лишь некоторые из множества возможностей кастомизации PDF-просмотрщика на базе pdf.js. Комбинируя различные функции и настраивая внешний вид под ваши требования, вы можете создать по-настоящему уникальный и функциональный просмотрщик документов для вашего сайта. 🚀
Решение типичных проблем при работе с pdf.js
Несмотря на все преимущества pdf.js, при его использовании могут возникать определённые сложности. В этом разделе рассмотрим наиболее распространённые проблемы и эффективные способы их решения. 🛠️
Проблема 1: CORS-ограничения при загрузке PDF с другого домена
Одна из самых частых проблем — ограничения кросс-доменных запросов при попытке загрузить PDF с другого домена.
Решения:
- Настройка CORS-заголовков на сервере: Если у вас есть доступ к серверу, откуда загруляется PDF, добавьте соответствующие заголовки:
Access-Control-Allow-Origin: *
# или для конкретного домена
Access-Control-Allow-Origin: https://yourwebsite.com
- Создание прокси на вашем сервере: Если нет возможности изменить настройки исходного сервера, создайте прокси на вашем бэкенде, который будет загружать PDF и передавать его клиенту.
- Предзагрузка на стороне сервера: Загружайте PDF на ваш сервер заранее, чтобы клиент получал его с того же домена.
Проблема 2: Производительность при работе с большими документами
PDF-файлы большого объёма или с высоким разрешением могут вызывать проблемы с производительностью, особенно на мобильных устройствах.
Решения:
- Отложенная загрузка страниц: Загружайте только те страницы, которые видны пользователю или находятся рядом в последовательности:
// Загружаем только видимые страницы и несколько следующих
function loadVisiblePages() {
const visiblePageNum = Math.ceil(container.scrollTop / pageHeight) + 1;
// Загружаем текущую и следующие 2 страницы
for (let i = visiblePageNum; i < visiblePageNum + 3 && i <= totalPages; i++) {
if (!loadedPages.includes(i)) {
renderPage(i);
loadedPages.push(i);
}
}
}
// Слушаем событие прокрутки
container.addEventListener('scroll', loadVisiblePages);
- Снижение качества рендеринга: Для предварительного просмотра используйте более низкое разрешение, повышая его только для текущей просматриваемой страницы.
- Разделение больших документов: Для очень больших документов рассмотрите возможность их разделения на несколько меньших файлов.
Проблема 3: Неправильное отображение шрифтов
Некоторые PDF-документы используют нестандартные шрифты, которые могут некорректно отображаться в pdf.js.
Решения:
- Встраивание шрифтов в PDF: Если вы создаёте PDF, убедитесь, что все используемые шрифты встроены в документ.
- Использование cmapUrl в настройках:
pdfjsLib.getDocument({
url: pdfUrl,
cMapUrl: 'https://unpkg.com/pdfjs-dist/cmaps/',
cMapPacked: true
}).promise.then(...)
Проблема 4: Ошибки при загрузке PDF
Возможные причины: поврежденный файл, несовместимые функции PDF или проблемы с сетевым соединением.
Решения:
- Проверка целостности файла: Убедитесь, что PDF-файл не поврежден, открыв его в стандартном просмотрщике.
- Обработка ошибок: Всегда добавляйте обработку ошибок для предоставления пользователю информативных сообщений:
loadingTask.promise.catch(error => {
console.error('Ошибка при загрузке PDF:', error);
let errorMessage;
switch (error.name) {
case 'InvalidPDFException':
errorMessage = 'Недопустимый или поврежденный PDF-файл.';
break;
case 'MissingPDFException':
errorMessage = 'PDF-файл не найден.';
break;
case 'UnexpectedResponseException':
errorMessage = 'Непредвиденный ответ сервера.';
break;
default:
errorMessage = `Произошла ошибка: ${error.message}`;
}
container.innerHTML = `<div class="pdf-error">${errorMessage}</div>`;
});
- Повторные попытки загрузки: Реализуйте механизм повторных попыток загрузки при сетевых проблемах.
Проблема 5: Проблемы с защищёнными PDF-документами
PDF-документы с защитой паролем или ограничениями могут вызвать проблемы при отображении.
Решения:
- Запрос пароля: Реализуйте диалоговое окно для запроса пароля:
pdfjsLib.getDocument({
url: pdfUrl,
password: userProvidedPassword // Пароль, введенный пользователем
}).promise.then(...)
- Проверка прав доступа: Проверяйте разрешения документа перед попыткой выполнения определенных операций:
pdfDocument.getPermissions().then(permissions => {
if (permissions & pdfjsLib.PermissionFlag.PRINT) {
// Разрешена печать
printButton.disabled = false;
} else {
printButton.disabled = true;
printButton.title = 'Печать запрещена для этого документа';
}
});
Проблема 6: Несовместимость с определенными браузерами
Хотя pdf.js стремится быть кроссбраузерным, могут возникать проблемы с определенными браузерами.
Решения:
- Проверка возможностей браузера: Добавьте проверку совместимости перед инициализацией pdf.js.
- Предложение альтернатив: Для несовместимых браузеров предлагайте альтернативные способы просмотра, например, прямую ссылку для скачивания.
- Использование полифилов: Для старых браузеров добавьте необходимые полифилы.
Зная эти типичные проблемы и способы их решения, вы сможете эффективнее интегрировать pdf.js в ваши проекты и обеспечить плавную работу PDF-просмотрщика для всех пользователей вашего сайта. 💪
Внедрение PDF-просмотрщика на базе pdf.js значительно повышает удобство работы с документами на вашем сайте. Эта библиотека предлагает идеальный баланс между функциональностью и гибкостью настройки. Начните с базовой интеграции, постепенно добавляя расширенные функции в соответствии с потребностями вашего проекта. Помните о потенциальных проблемах и их решениях, особенно при работе с большими документами или кросс-доменными запросами. Правильно настроенный PDF-просмотрщик станет мощным инструментом, который выделит ваш сайт среди конкурентов и значительно улучшит взаимодействие пользователей с вашим контентом.