Категория

Уменьшение размера PDF: изображения, шрифты и потоки контента в JavaScript (React)

2026-09-18 08:27:30 Allen Yang
AI Summarize:
ChatGPT
ChatGPT ✓
Claude ✓
Grok ✓
Perplexity ✓
Quick
Quick
Concise overview
Highlights
Key takeaways
Detailed
Structured explanation
Brief
One sentence summary
Summarize |

Compressing the images, fonts and content streams of a PDF in the browser with Spire.PDF for JavaScript

PDF редко бывает просто «слишком большим». Он слишком большой для одной конкретной двери — ограничения на вложение в почте, формы загрузки, квоты хранилища — и то, что вытолкнуло его за этот предел, обычно является одной из трёх вещей. Сжимать, не зная, какой именно, — значит гадать, а гадание стоит либо качества, либо ещё одной неудачной загрузки.

Spire.PDF for JavaScript даёт вам компрессор с опцией для каждого из этих трёх мест. Их можно применять по отдельности или вместе, и компромиссы достаточно различны, чтобы стоило понять каждый, прежде чем решать. Всё выполняется в браузере на WebAssembly, а файлы перемещаются через виртуальную файловую систему (VFS), поэтому документ не нужно загружать в сторонний сервис, чтобы уменьшить его размер.

По настройке проекта см. Интеграция Spire.PDF for JavaScript в проект React. Примеры ниже предполагают, что пакет установлен и модуль WebAssembly инициализирован.


Что на самом деле делает PDF большим

Размер файла PDF складывается из трёх разных мест, и ведут они себя совершенно по-разному:

Где сосредоточен вес Как это выглядит Признак, что причина в этом
Изображения Растровые изображения, встроенные в собственном разрешении — сканы, фотографии, экспортированные скриншоты Мало страниц, десятки мегабайт
Встроенные шрифты Шрифтовые программы, хранящиеся внутри файла, чтобы текст одинаково отображался везде В основном текст, но всё ещё несколько мегабайт
Потоки содержимого Инструкции рисования, стоящие за текстом и векторной графикой Файл редактировался или пересохранялся несколькими разными инструментами

Два документа одинаковой длины могут различаться на порядок из-за этого: пятистраничный сканированный договор может весить 20 МБ, тогда как пятидесятистраничный текстовый отчёт укладывается в 1 МБ. Вот почему у вопроса «насколько я могу это сжать?» нет единственного ответа — файл с обилием сканов может многое вернуть, а документ, который уже в основном состоит из текста и векторной графики, близок к своему минимуму.

Поэтому сначала нужна диагностика. Сжатие документа с большим объёмом текста настройками изображений почти ничего не даёт, а сжатие изображений в документе, вес которого на самом деле в шрифтах, тратит качество без выигрыша.


Три рычага сжатия

PdfCompressor предоставляет свою стратегию через Options, и каждая группа настроек соответствует одному из трёх источников выше:

Параметр На что нацелен Чем вы жертвуете
ImageCompressionOptions Растровые изображения на странице Точность изображений — это единственный рычаг с потерями из трёх
TextCompressionOptions Данные встроенных шрифтов Ничем, если только вы не удаляете встроенные шрифты — тогда переносимостью
CompressContents Потоки содержимого страницы Практически ничем — содержимое перекодируется, а не отбрасывается

Следующие три раздела рассматривают их по одному, чтобы эффект каждого был виден отдельно. Раздел после них объединяет их, что обычно и нужно в продакшене.


Сжатие изображений

Растровые изображения с высоким разрешением — самая распространённая единственная причина PDF завышенного размера, и именно там наибольший выигрыш. Options.ImageCompressionOptions управляет масштабированием и перекодированием через три свойства:

Свойство Что делает Когда стоит устанавливать
ResizeImages Пропорционально уменьшает изображения, затем перекодирует их Фотографии или сканы, снятые с разрешением намного выше того, при котором они отображаются
CompressImage Применяет к изображениям перекодирование с потерями Когда качество изображений вообще можно немного снизить
ImageQuality Выбирает уровень качества для этого перекодирования Последняя настройка — High сохраняет больше деталей, Low даёт меньший файл
// Compress images in the document: resize, re-compress, and lower the quality
compressor.Options.ImageCompressionOptions.ResizeImages = true;
compressor.Options.ImageCompressionOptions.CompressImage = true;
compressor.Options.ImageCompressionOptions.ImageQuality = pdfModule.ImageQuality.Low;

ImageQuality сам по себе ничего не делает — он лишь описывает, как кодируются изображения, проходящие через первые два свойства, поэтому установка только его возвращает вам тот же файл. И поскольку это единственный рычаг с потерями, сначала протестируйте на уровне High, если PDF будет печататься, а не читаться на экране.


Шрифты: сжимать или удалять встроенные

Чтобы текст одинаково отображался на машинах, где нет исходных шрифтов, PDF содержит шрифтовые программы, используемые в его тексте, внутри файла. Они могут быть подмножествами, но отсутствующими не являются — а несколько семейств с несколькими начертаниями в сумме дают немало. Options.TextCompressionOptions предлагает два совершенно разных способа справиться с этим:

Свойство Что делает Подвох
CompressFonts Сжимает данные встроенных шрифтов и оставляет шрифты на месте Нет для отображения — глифы остаются в файле
UnembedFonts Удаляет шрифтовые программы и позволяет программе просмотра подставлять системные шрифты Подмена глифов или смещение интервалов там, где этих шрифтов нет
// Compress font data; UnembedFonts goes further and removes the embedded fonts
compressor.Options.TextCompressionOptions.CompressFonts = true;
compressor.Options.TextCompressionOptions.UnembedFonts = true;

Комбинация выше — агрессивная, и она безопасна ровно при одном условии: вы знаете, что будет отображать документ. Ссылка для скачивания или письмо неизвестному получателю — это не такое условие; там оставьте шрифты встроенными и дайте CompressFonts сделать работу. Удаляйте встроенные шрифты только там, где набор шрифтов фиксирован, например во внутренней программе просмотра с известной конфигурацией.


Повторное сжатие потоков содержимого

Текст и команды векторной отрисовки на странице находятся в потоках содержимого. Они сжимаются при записи PDF, но документ, который многократно редактировался или передавался между разными инструментами, может накапливать в них избыточность. Options.CompressContents перекодирует эти потоки:

// Re-compress the document content streams
compressor.Options.CompressContents = true;

Сам по себе это наименее впечатляющий из трёх — не ожидайте, что он спасёт документ с обилием фотографий. Он оправдывает себя в насыщенных текстом документах со сложной вёрсткой, и это самый безопасный рычаг, который можно оставить включённым, поскольку перекодирование потока не меняет то, что он рисует.


Все три за один проход

Эти три измерения — не три отдельных запуска. Вы направляете один PdfCompressor на файл, задаёте столько опций, сколько нужно, и записываете результат один раз с помощью CompressToFile.

Пример ниже включает изменение размера и повторное сжатие изображений на уровне High, сжатие шрифтов с удалением встроенных и повторное сжатие потоков содержимого — для документа, содержащего как изображения с высоким разрешением, так и основной текст:

function App() {
  const compressPdfDocument = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check that the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Load the PDF to be compressed into the VFS
    const inputFileName = 'ImageDocument.pdf';
    await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);

    // Create a PdfCompressor and point it at the PDF to compress
    let compressor = new pdfModule.PdfCompressor({ filePath: inputFileName });

    // 1. Image compression: resize and re-compress images, with a higher quality tier
    compressor.Options.ImageCompressionOptions.ResizeImages = true;
    compressor.Options.ImageCompressionOptions.CompressImage = true;
    compressor.Options.ImageCompressionOptions.ImageQuality = pdfModule.ImageQuality.High;

    // 2. Font compression: compress font data and remove embedded fonts
    compressor.Options.TextCompressionOptions.CompressFonts = true;
    compressor.Options.TextCompressionOptions.UnembedFonts = true;

    // 3. Content compression: re-compress the document content streams
    compressor.Options.CompressContents = true;

    // Define the output file name and compress to it
    const outputFileName = 'CompressedDocument.pdf';
    compressor.CompressToFile(outputFileName);

    // Read the generated file from the VFS and trigger the 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>Compress PDF Document</h1>
      <button onClick={compressPdfDocument}>
        Generate
      </button>
    </div>
  );
}

export default App;

PDF-документ, сжатый сочетанием всех трёх подходов

PDF document compressed by combining all three approaches

Два замечания о форме этого кода. Оригинал остаётся в VFS нетронутым — CompressToFile записывает новый документ, поэтому доступен выбор до и после. И поскольку компрессор работает с ресурсами документа, а не с отрендеренными страницами, текст остаётся настоящим текстом: всё ещё выделяемым, всё ещё доступным для поиска. Перекодируются только изображения.


Проверка изменений

Оба файла находятся в VFS, поэтому вы можете сравнить их, прежде чем отдавать результат пользователю, вместо того чтобы гадать, дали ли настройки какой-либо эффект:

// Both the original and the compressed copy are in the VFS
const originalBytes = window.dotnetRuntime.Module.FS.readFile(inputFileName).length;
const compressedBytes = window.dotnetRuntime.Module.FS.readFile(outputFileName).length;

Сообщайте разницу в процентах рядом с загрузкой, а не предлагайте голую кнопку — это сразу говорит пользователю, достаточно ли файл мал для того места, куда он направляется. Если два числа почти одинаковы, вес был не там, где вы сжимали.


Подбор настроек под документ

Документ Включить На что обратить внимание
Сканированные страницы, фотографии, скриншоты ResizeImages, CompressImage, ImageQuality, настроенные под экран или печать Мелкие детали внутри скана размываются первыми — проверьте на High перед публикацией
В основном текст и векторная графика CompressFonts, CompressContents Выигрыш по своей природе скромный; не гонитесь за ним с помощью настроек изображений
Смешанное содержимое Все три, при ImageQuality.High Самый безопасный универсальный профиль, и обычно лучшее место для начала
Уже оптимизирован для распространения Только CompressContents Мало что можно выиграть; агрессивное перекодирование изображений может стоить качества почти ни за что

Частые проблемы

Файл почти не уменьшился, а в PDF почти нет изображений. Размер идёт из шрифтов или потоков содержимого. Включите TextCompressionOptions — с UnembedFonts только если среда просмотра известна — и добавьте CompressContents. Насыщенные текстом документы реагируют на эти две настройки и ни на что другое.

Текст отображается иначе после сжатия. Это UnembedFonts делает ровно то, что заявлено. Верните его в false и оставьте CompressFonts = true; данные шрифтов всё равно сжимаются, но глифы остаются в файле.

Изображения выглядят размытыми или блочными в выходном файле. Либо уровень ImageQuality слишком низкий, либо ResizeImages уменьшил изображение, которому для печати нужно было его разрешение. Для всего, что будет печататься, начинайте с High.

Сжатый файл такого же размера, как оригинал. Некоторые документы уже оптимизированы и не имеют ничего значимого, что можно отдать. Проверьте с помощью сравнения байтов выше, вместо того чтобы предполагать, что настройки не сработали.

Ничего не происходит при первом клике. Модуль WebAssembly загружается асинхронно. Для этого и нужна проверка if (!pdfModule) return; — в реальном приложении отключайте кнопку, пока модуль не сообщит о готовности, вместо показа предупреждения.


Часто задаваемые вопросы

За какой рычаг взяться первым?

За изображения, если предполагать, что виноваты они — но подтвердите это. Если документ насыщен текстом и непропорционален своему количеству страниц, начните со шрифтов и потоков содержимого и не трогайте настройки изображений.

Делает ли сжатие PDF его текст нечитаемым или невыделяемым?

Нет, и именно здесь компрессор на основе API отличается от онлайн-инструментов, которые растрируют каждую страницу в JPEG. Текст остаётся текстом, векторная графика остаётся векторной, и затрагиваются только растровые изображения — поэтому сжатая копия всё ещё выделяемая, доступная для поиска и пригодная для печати.

Можно ли сжать только часть документа?

Компрессор работает с документом как с целым. Если вам нужно уменьшить часть файла, а часть оставить нетронутой, сначала разделите документ, сожмите нужный фрагмент и снова объедините.

Можно ли сохранить оригинал и предложить сжатую копию на выбор?

Да. CompressToFile записывает в новое имя файла и оставляет входной файл в VFS, поэтому обе версии доступны в одной сессии — это полезно, когда пользователь решает, сколько качества обменять.

Нужен ли для этого сервер, или это может работать полностью в браузере?

Полностью в браузере. PDF-движок скомпилирован в WebAssembly, а файлы перемещаются через VFS, поэтому документ никогда не покидает устройство. Это особенно важно именно для тех документов, которые люди обычно хотят уменьшить — договоров, выписок, записей.


См. также