Категория

Обрезка страниц PDF: удаление полей и пустого пространства с помощью JavaScript

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

Обе страницы обрезаны с отступом 60 пунктов; внешнее белое поле и рамка удалены

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

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


CropBox и MediaBox: объяснение двух границ страницы

Каждая страница PDF определяется двумя прямоугольниками, и понимание разницы между ними необходимо перед началом обрезки.

MediaBox описывает физические размеры страницы — полный лист бумаги, так сказать. Это внешняя граница, и она определяет систему координат, в которой размещается всё содержимое. Стандартная страница A4 имеет MediaBox примерно 595 × 842 пункта.

CropBox определяет, что фактически отображается в просмотрщике. Это подобласть MediaBox, и любое содержимое за пределами CropBox скрыто от просмотра. По умолчанию CropBox совпадает с MediaBox, поэтому обычно видна вся страница. Когда вы уменьшаете CropBox, вы фактически сообщаете программе просмотра PDF: "Показывать только эту часть страницы."

Ключевой момент для обрезки: CropBox производен от MediaBox. Вы считываете полные размеры страницы из page.MediaBox.Width и page.MediaBox.Height, затем вычисляете меньший прямоугольник — с отступом на нужное значение с каждой стороны — и присваиваете его page.CropBox. Координаты x и y CropBox отсчитываются от верхнего левого угла страницы, а width и height определяют, какая часть страницы сохраняется.


Обрезка всех страниц с одинаковым отступом

Наиболее распространённый сценарий — применение одинакового уменьшения отступа к каждой странице документа. Вы перебираете doc.Pages, считываете MediaBox каждой страницы и задаёте CropBox с отступом на фиксированное число пунктов со всех четырёх сторон.

В приведённом ниже примере с каждого края каждой страницы срезается 60 пунктов — этого достаточно, чтобы удалить широкую белую рамку или ненужную границу:

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

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

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

    // Create a PdfDocument object and load the PDF document
    let doc = new pdfModule.PdfDocument();
    doc.LoadFromFile(inputFileName);

    // Trim 60 points off every side
    const margin = 60;

    for (let i = 0; i < doc.Pages.Count; i++) {
      const page = doc.Pages.get_Item(i);

      // MediaBox gives the full extent of the page, from which the crop box is derived
      // (x and y are measured from the top-left corner of the page)
      const width = page.MediaBox.Width;
      const height = page.MediaBox.Height;

      page.CropBox = new pdfModule.RectangleF({
        x: margin,
        y: margin,
        width: width - margin * 2,
        height: height - margin * 2,
      });
    }

    const outputFileName = 'CropByMargins.pdf';
    doc.SaveToFile(outputFileName);
    doc.Close();

    // 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>Crop PDF Pages</h1>
      <button onClick={cropPdfPage}>
        Crop PDF
      </button>
    </div>
  );
}

export default App;

Обе страницы обрезаны с отступом 60 пунктов, поэтому белое поле и рамка вокруг страницы удалены:

Обе страницы обрезаны с отступом 60 пунктов; внешнее белое поле и рамка удалены

Настройте значение margin, чтобы управлять степенью обрезки страницы. Большее значение удаляет больше пустого пространства; меньшее — выполняет более тонкую обрезку. Поскольку отступ применяется единообразно ко всем четырём сторонам, этот подход лучше всего работает, когда нежелательная граница примерно одинакова по всем краям — что обычно бывает со сканированными документами и экспортированными чертежами.


Обрезка одной страницы и всего документа

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

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

Когда обрезать одну страницу: Уберите цикл и назначьте CropBox непосредственно целевой странице. Это полезно, когда нужно обрезать только одну страницу — например, титульную страницу с слишком большим логотипом или счёт, где должна быть видна только область таблицы на странице 2. Вы также можете применять разные значения обрезки к разным страницам, комбинируя логику для каждой страницы внутри цикла.

Вот как обрезать только первую страницу, сохранив блок размером 400 × 500 пунктов, начинающийся в позиции (80, 80) от верхнего левого угла:

// Crop page 1 only: keep a 400 x 500 point block starting at (80, 80) from the top-left corner
const page = doc.Pages.get_Item(0);
page.CropBox = new pdfModule.RectangleF({ x: 80, y: 80, width: 400, height: 500 });

Значения x и y указывают, где начинается видимая область, отсчитываясь от верхнего левого угла страницы. Значения width и height определяют размер этой области. Всё, что находится за пределами этого прямоугольника, скрыто от просмотрщика.


Мягкая обрезка: что происходит с содержимым

Есть важная деталь о CropBox, которую легко упустить: он выполняет мягкую обрезку, а не жёсткую.

Когда вы задаёте CropBox, вы меняете видимую границу страницы — прямоугольник, который отображают программы просмотра PDF и используют принтеры как область страницы. Но содержимое, выходящее за эту границу, не удаляется из файла. Оно по-прежнему там, просто скрыто. Это имеет два практических последствия:

  1. Размер файла не уменьшается. Обрезанные текст, изображения и векторная графика остаются в потоке данных PDF. Если ваша цель — уменьшить размер файла за счёт обрезки полей, одна только установка CropBox этого не даст.

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

Если вам нужно действительно удалить содержимое — для редактирования, уменьшения размера файла или гарантии того, что скрытый текст невозможно восстановить — необходимо пересобрать страницу. Подход таков: создайте новый документ, извлеките видимое содержимое из обрезанной страницы с помощью page.CreateTemplate(), нарисуйте его на новой странице в новом документе и сохраните результат. Это даёт жёсткую обрезку, при которой содержимое за пределами границ больше не существует в файле.

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


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

Можно ли обрезать одну страницу вместо всего документа?

Да. Поскольку CropBox — это свойство отдельной страницы, встроенного метода "обрезать весь документ" нет — цикл в основном примере просто удобен для применения одинакового отступа ко всем страницам. Чтобы обрезать только одну страницу, пропустите цикл и назначьте CropBox напрямую этой странице:

// Crop page 1 only: keep a 400 x 500 point block starting at (80, 80) from the top-left corner
const page = doc.Pages.get_Item(0);
page.CropBox = new pdfModule.RectangleF({ x: 80, y: 80, width: 400, height: 500 });

Как отменить обрезку?

Установка CropBox изменяет границу страницы на месте, и документ не сохраняет предыдущую границу. Можно ожидать, что присваивание page.MediaBox обратно page.CropBox восстановит исходный вид, но это не работает как задумано — координаты применяются относительно начала текущей видимой области, поэтому изменяются только размеры, а начало остаётся фиксированным. После обрезки на 60 пунктов переназначение размеров MediaBox по-прежнему оставляет видимую область начинающейся с (60, 60).

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

// Undo when the crop offset was (offsetX, offsetY)
page.CropBox = new pdfModule.RectangleF({
  x: -offsetX,
  y: -offsetY,
  width: page.MediaBox.Width,
  height: page.MediaBox.Height,
});

Файл не стал меньше, а обрезанное содержимое всё ещё доступно для поиска — почему?

Это ожидаемое поведение. CropBox выполняет мягкую обрезку: он изменяет только видимую границу страницы, тогда как содержимое за её пределами остаётся в файле и всё ещё может быть найдено текстовым поиском или скопировано. Если вам нужно физически удалить содержимое из PDF, установки CropBox недостаточно — потребуется пересобрать страницу, создав новый документ, извлекая видимое содержимое с помощью page.CreateTemplate(), рисуя его на новой странице и сохраняя в новый файл.


См. также