PDF 페이지 자르기: JavaScript로 여백과 공백 크롭하기

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

Both pages cropped by a 60-point margin, removing the outer white margin and frame

PDF는 필요한 것보다 더 많은 여백을 포함하는 경우가 많습니다 — 두꺼운 테두리가 있는 스캔 문서, 여유로운 여백이 있는 엔지니어링 도면, 또는 중앙의 표만 중요한 인보이스 등이 그렇습니다. 페이지를 자르는 것이 자연스러운 해결책이지만, 브라우저 기반 워크플로에서 이를 수행하는 것은 간단하지 않습니다. 데스크톱 도구는 웹 경험을 깨뜨리고, 파일을 백엔드 서버로 보내는 것은 개인정보 및 규정 준수 문제를 일으킬 수 있습니다.

이때 Spire.PDF for JavaScript가 등장합니다. 이는 WebAssembly에서 실행되며 전적으로 브라우저 내부에서 동작하여, 서버 왕복 없이 가상 파일 시스템(VFS)을 통해 PDF를 로드하고 저장합니다. 각 페이지의 표시 영역을 프로그래밍 방식으로 설정하고 사용자가 잘린 결과를 다운로드하도록 할 수 있습니다 — 모두 클라이언트 측에서 말입니다. 설치 및 프로젝트 설정은 React 프로젝트에서 Spire.PDF for JavaScript 통합하기를 참조하십시오. 아래 예제는 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에 할당합니다. CropBox의 x 및 y 좌표는 페이지의 왼쪽 위 모서리에서 측정되며, 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포인트 여백으로 잘려서 페이지 주변의 흰색 여백과 프레임이 제거됩니다:

Both pages cropped by a 60-point margin, removing the outer white margin and frame

margin 값을 조정하여 페이지가 얼마나 공격적으로 잘릴지 제어합니다. 값이 클수록 더 많은 여백이 제거되고, 값이 작을수록 더 미묘하게 잘립니다. 여백이 네 변 모두에 균일하게 적용되므로, 이 접근 방식은 원치 않는 테두리가 모든 변에서 거의 동일할 때 가장 잘 작동합니다 — 일반적으로 스캔 문서와 내보낸 도면에서 그렇습니다.


단일 페이지 대 전체 문서 자르기

이전 섹션의 루프는 모든 페이지에 동일한 자르기를 적용합니다. 하지만 CropBox는 페이지 수준 속성이므로 문서 전체에 적용되는 자르기 인터페이스는 없습니다. 루프는 단순히 각 페이지가 동일한 여백을 받도록 보장합니다. 이 구분은 페이지마다 요구 사항이 다를 때 중요합니다.

전체 문서를 자를 때: 모든 페이지가 동일한 문제를 공유할 때 루프 접근 방식을 사용하십시오 — 예를 들어 모든 페이지에 동일한 스캐너 테두리가 있는 스캔 배치나, 동일한 여백으로 내보낸 여러 페이지 도면 세트가 그렇습니다. 단일 margin 값을 사용하면 코드가 단순해지고 결과가 일관됩니다.

단일 페이지를 자를 때: 루프를 제거하고 대상 페이지에 CropBox를 직접 할당하십시오. 한 페이지만 잘라야 할 때 유용합니다 — 예를 들어 너무 큰 로고가 있는 표지 페이지나, 2페이지의 표 영역만 표시되어야 하는 인보이스가 그렇습니다. 루프 내부에 페이지별 로직을 결합하여 페이지마다 다른 자르기 값을 적용할 수도 있습니다.

다음은 첫 번째 페이지만 자르는 방법으로, 왼쪽 위 모서리에서 (80, 80) 위치에서 시작하는 400 × 500포인트 블록을 유지합니다:

// 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 밖의 텍스트는 검색 기능으로 여전히 찾을 수 있으며, 표시 영역 너머를 선택하는 방법을 아는 사용자가 복사할 수 있습니다. 이는 일반적으로 여백 자르기에는 괜찮지만, 자르기가 민감한 정보를 삭제하거나 안전하게 제거하는 방법이 아니라는 의미입니다.

콘텐츠를 진정으로 제거해야 하는 경우 — 삭제(redaction), 파일 크기 축소 또는 숨겨진 텍스트를 복구할 수 없도록 보장하기 위해 — 페이지를 다시 작성해야 합니다. 방법은 새 문서를 만들고, 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()으로 표시되는 콘텐츠를 추출하고, 새 페이지에 그린 다음 새 파일로 저장하여 페이지를 다시 작성해야 합니다.


참고 항목