
Когда ваше React-приложение создаёт PDF-файлы на лету — счёт-фактуру, на которую нужно поместить логотип компании, отчёт со встроенной диаграммой или сертификат с подписью, — вам приходится программно размещать растровые изображения на странице. Обычный JavaScript не умеет записывать данные во внутреннюю структуру PDF, а поднимать бэкенд только ради добавления логотипа — избыточно для задачи, которая на самом деле решается на клиентской стороне.
Spire.PDF для JavaScript компилирует полноценный PDF-движок в WebAssembly, поэтому ваше React-приложение может создавать, редактировать и сохранять PDF-файлы полностью в браузере. Файлы передаются через встроенную в браузер виртуальную файловую систему (VFS), благодаря чему не требуется сетевой обмен данными. Используя PdfImage и метод DrawImage на холсте страницы, вы управляете точным расположением и размером каждого изображения.
В этой статье вы узнаете, как:
- Загрузить изображение в VFS и преобразовать его в объект
PdfImage - Нарисовать его на странице PDF в выбранной позиции и с заданным размером
- Добавлять изображения как в новые, так и в существующие PDF-документы
- Масштабировать, центрировать и повторять изображения на нескольких страницах
- Запустить скачивание готового PDF-файла в браузере
Зачем создавать PDF-файлы в браузере
Обычная альтернатива — серверная библиотека (iText, PDFBox и т. п.): браузер загружает ресурсы на сервер, сервер выполняет рендеринг, а результат возвращается обратно. Такой подход работает, но при встраивании изображений создаёт дополнительные трудности:
- Задержка — каждый рендер требует обмена данными по сети, что особенно ощутимо для больших файлов или медленных соединений.
- Конфиденциальность — исходные документы и изображения покидают устройство пользователя, что критично для любых чувствительных данных.
- Стоимость — рендеринг PDF требует значительных вычислительных ресурсов и не масштабируется бесплатно.
С помощью Spire.PDF для JavaScript вся задача выполняется в браузере через WebAssembly. После загрузки WASM-модуля рендеринг происходит локально и мгновенно, файл не покидает устройство, а серверное время не расходуется.
Предварительные требования
В этом руководстве предполагается, что у вас уже создан React-проект с установленным Spire.PDF для JavaScript и инициализированным WASM-модулем. Если нет, сначала выполните инструкции из статьи Интеграция Spire.PDF для JavaScript в проект React.
Вам понадобятся:
- Файлы
spire.pdf.base.jsиspire.pdf.base.wasmв папкеpublicвашего проекта - Модуль WASM, доступный через
window.wasmModule.spirepdf - Изображение, которое нужно встроить (PNG, JPEG и т. д.), расположенное там, откуда VFS сможет его загрузить
Добавление изображения в новый PDF
Самый простой сценарий: создать абсолютно новый PDF-документ и нарисовать изображение на его первой странице. Основные шаги выглядят так:
- Загрузите изображение в VFS, используя
window.spire.FetchFileToVFS - Создайте объект
PdfDocumentи добавьте пустую страницу - Создайте объект
PdfImageиз загруженного файла с помощьюPdfImage.FromFile - Нарисуйте изображение на холсте страницы с помощью
page.Canvas.DrawImage - Сохраните и скачайте результат
function App() {
const addImageToPdf = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check if the WASM module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the image into VFS
const inputImageName = 'TreePic.png';
await window.spire.FetchFileToVFS(inputImageName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object
let doc = new pdfModule.PdfDocument();
// Add a page
let page = doc.Pages.Add();
// Load the image and scale its display size proportionally
let image = pdfModule.PdfImage.FromFile(inputImageName);
let width = image.Width * 0.6;
let height = image.Height * 0.6;
// Calculate the horizontal center position and set the vertical position
let x = (page.Canvas.ClientSize.Width - width) / 2;
let y = 60;
// Draw the image at the specified position on the page
page.Canvas.DrawImage({ image: image, x: x, y: y, width: width, height: height });
// Define the output file name in PDF format
const outputFileName = 'AddImage.pdf';
// Save as PDF format
doc.SaveToFile({ fileName: outputFileName });
doc.Close();
// Read the generated PDF file from VFS and trigger download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Add Image To PDF</h1>
<button onClick={addImageToPdf}>
Generate
</button>
</div>
);
}
export default App;
PDF-документ, созданный после добавления изображения

Что делает этот код:
-
PdfImage.FromFile(inputImageName)читает изображение из VFS и создаёт объектPdfImage. Исходные размеры в пикселях доступны черезimage.Widthиimage.Height. -
page.Canvas.DrawImage(...)выводит изображение на страницу. Параметрыxиyзадают позицию верхнего левого угла, аwidthиheight— размер отображения. - Изображение масштабируется до 60% исходного размера (
* 0.6) и центрируется по горизонтали с помощью выражения(page.Canvas.ClientSize.Width - width) / 2.
Добавление изображения в существующий PDF
Добавление изображения в существующий документ выполняется по той же схеме — единственное отличие состоит в том, что вместо создания нового PdfDocument вы загружаете документ из VFS и выбираете нужную страницу.
const addImageToExistingPdf = async () => {
const pdfModule = window.wasmModule?.spirepdf;
if (!pdfModule) return;
// Load both the PDF and the image into VFS
await window.spire.FetchFileToVFS('Report.pdf', "", `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS('Logo.png', "", `${process.env.PUBLIC_URL}/data/`);
// Load the existing PDF
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile('Report.pdf');
// Get the first page (or any page you want)
let page = doc.Pages.get_Item(0);
// Load the image and draw it at the top-right corner
let image = pdfModule.PdfImage.FromFile('Logo.png');
let imgWidth = 80;
let imgHeight = 40;
let x = page.Canvas.ClientSize.Width - imgWidth - 30; // 30pt margin from right edge
let y = 30; // 30pt from top
page.Canvas.DrawImage({ image: image, x: x, y: y, width: imgWidth, height: imgHeight });
// Save and download
const outputFileName = 'ReportWithLogo.pdf';
doc.SaveToFile({ fileName: outputFileName });
doc.Close();
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
Ключевое отличие от примера с новым документом: doc.LoadFromFile('Report.pdf') загружает существующий PDF вместо создания пустого документа, а doc.Pages.get_Item(0) получает страницу из загруженного документа. Вся остальная логика отрисовки идентична.
Если на странице уже есть изображение, которое нужно изменить, а не наложить поверх новое, обратитесь к статье Замена и удаление изображений из PDF на JavaScript (React).
Масштабирование и позиционирование
Метод DrawImage даёт полный контроль над тем, где и в каком размере будет отображаться изображение. Ниже приведены самые частые сценарии:
Пропорциональное масштабирование — умножьте обе стороны на один и тот же коэффициент, чтобы сохранить пропорции:
let scale = 0.5; // 50% of original size
let width = image.Width * scale;
let height = image.Height * scale;
Фиксированная ширина и автоматическая высота — задайте ширину и вычислите высоту, сохранив соотношение сторон:
let targetWidth = 200;
let width = targetWidth;
let height = image.Height * (targetWidth / image.Width);
Центрирование по горизонтали — поместите изображение на одинаковом расстоянии от левого и правого краёв страницы:
let x = (page.Canvas.ClientSize.Width - width) / 2;
Центрирование по вертикали — поместите изображение на одинаковом расстоянии от верхнего и нижнего краёв страницы:
let y = (page.Canvas.ClientSize.Height - height) / 2;
Произвольное положение — используйте абсолютные координаты (начало координат — в верхнем левом углу, единицы измерения — пункты; 1 пункт = 1/72 дюйма):
let x = 72; // 1 inch from left
let y = 144; // 2 inches from top
Добавление изображений на несколько страниц
Чтобы добавить одинаковое изображение (например, логотип или водяной знак) на каждую страницу документа, выполните перебор коллекции Pages:
const addImageToAllPages = async () => {
const pdfModule = window.wasmModule?.spirepdf;
if (!pdfModule) return;
await window.spire.FetchFileToVFS('Business_Data_Overview.pdf', "", `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS('Logo.png', "", `${process.env.PUBLIC_URL}/data/`);
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile('Business_Data_Overview.pdf');
let image = pdfModule.PdfImage.FromFile('Logo.png');
let imgWidth = 60;
let imgHeight = 30;
// Loop through all pages and draw the logo in the top-right corner
for (let i = 0; i < doc.Pages.Count; i++) {
let page = doc.Pages.get_Item(i);
let x = page.Canvas.ClientSize.Width - imgWidth - 20;
let y = 20;
page.Canvas.DrawImage({ image: image, x: x, y: y, width: imgWidth, height: imgHeight });
}
const outputFileName = 'AllPagesWithLogo.pdf';
doc.SaveToFile({ fileName: outputFileName });
doc.Close();
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
Этот приём удобно использовать для добавления водяных знаков, логотипов компании или штампов на все страницы многостраничного PDF-документа.
Скачивание результата
После сохранения PDF в VFS с помощью doc.SaveToFile() необходимо прочитать его обратно и инициировать скачивание в браузере. Эта двухэтапная схема — сохранение в VFS, затем чтение из VFS — используется во всех примерах Spire.PDF для JavaScript:
// 1. Save the PDF to the VFS
doc.SaveToFile({ fileName: 'Output.pdf' });
doc.Close();
// 2. Read the file from VFS as a byte array
const fileArray = window.dotnetRuntime.Module.FS.readFile('Output.pdf');
// 3. Create a Blob and trigger download
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'Output.pdf';
a.click();
URL.revokeObjectURL(url);
Та же схема применяется при сохранении изображений (в конструкторе Blob укажите type: 'image/png' или type: 'image/jpeg').
Часто задаваемые вопросы
Как точно контролировать положение и размер изображения?
Параметры x и y метода DrawImage задают координаты верхнего левого угла изображения (в пунктах, где 1 пункт = 1/72 дюйма). Параметры width и height задают размер отображения. Чтобы масштабировать пропорционально, прочитайте image.Width и image.Height и умножьте оба значения на один и тот же коэффициент. Для центрирования по горизонтали вычислите x = (page.Canvas.ClientSize.Width - width) / 2.
Можно ли добавить на одну страницу несколько изображений?
Да. Вызывайте page.Canvas.DrawImage(...) для каждого изображения по отдельности, задавая разные координаты x/y. Изображения рисуются в том порядке, в котором вы вызываете метод, поэтому при перекрытии последующие изображения будут поверх предыдущих.
Какие форматы изображений поддерживаются?
Spire.PDF для JavaScript поддерживает распространённые растровые форматы: PNG, JPEG, BMP и GIF. Для загрузки любого из них из VFS используйте PdfImage.FromFile(filename).
Влияет ли добавление изображения на существующее содержимое страницы?
Нет. Метод DrawImage добавляет на страницу новый объект изображения, не изменяя существующий текст, графику и другие изображения. Изображение наносится поверх текущего содержимого в указанных координатах. Чтобы извлечь изображение из существующего PDF и использовать его в другом месте, обратитесь к статье Извлечение изображений из PDF на JavaScript (React).