전체 글

gpdf 에서 투명 PNG 를 임베드하는 방법

PNG 바이트를 그대로 c.Image 에 넘긴다. gpdf 가 알파 채널을 PDF SMask 로 변환해 투명 배경이 그대로 렌더된다.

질문을 다시 표현하면

로고나 도장을 배경 투명 PNG — Photoshop 이나 Figma 가 내보내는 RGBA PNG — 로 가지고 있다. 이걸 gpdf 의 PDF 에 임베드하면 투명 영역이 그대로 투명으로 남는가? 아니면 로고 주변에 흰색 박스가 생기는가?

빠른 답

PNG 바이트를 c.Image 에 넘기면 끝이다. 추가 옵션은 필요 없다. gpdf 가 알파 채널을 디코드해 PDF SMask (소프트 마스크) 객체를 이미지와 함께 작성한다. 투명 픽셀은 그대로 투명으로 렌더된다.

logo, _ := os.ReadFile("logo.png")
c.Image(logo, template.FitWidth(document.Mm(40)))

레시피는 이게 전부. 흰 배경에 알파를 평탄화할 필요도, RGBA 를 RGB 로 변환할 필요도, "투명도 활성화" 같은 옵션을 넘길 필요도 없다. PNG 는 PDF 까지 PNG 로 간다.

그대로 돌릴 수 있는 완성 예제

투명도를 실제로 보려면 PNG 아래에 비쳐 보일 무언가가 있어야 한다. 본문 위에 워터마크를 올리는 패턴이 가장 흔하고, page.Absolute 는 로고를 고정 좌표에 배치하면서 일반 흐름의 콘텐츠가 그 아래에 페이지를 채우게 해준다.

package main

import (
    "log"
    "os"

    "github.com/gpdf-dev/gpdf"
    "github.com/gpdf-dev/gpdf/document"
    "github.com/gpdf-dev/gpdf/template"
)

func main() {
    stamp, err := os.ReadFile("draft-stamp.png")
    if err != nil {
        log.Fatal(err)
    }

    doc := gpdf.NewDocument(
        gpdf.WithPageSize(gpdf.A4),
        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),
    )

    page := doc.AddPage()

    page.AutoRow(func(r *template.RowBuilder) {
        r.Col(12, func(c *template.ColBuilder) {
            c.Text("분기 보고서 — 2026 Q1", template.FontSize(20), template.Bold())
            c.Text("1 분기 매출은 전년 동기 대비 38% 증가했다. 엔터프라이즈 갱신과 금융 서비스 신규 고객 3 곳이 견인했다. 인프라 비용이 정체되며 영업이익률은 24% 까지 확대됐다.")
            c.Text("분기말 직원은 142 명. 4 분기말 128 명에서 14 명 순증, 그중 9 명이 엔지니어링 채용이다.")
        })
    })

    page.Absolute(document.Mm(60), document.Mm(120), func(c *template.ColBuilder) {
        c.Image(stamp, template.FitWidth(document.Mm(80)))
    })

    data, err := doc.Generate()
    if err != nil {
        log.Fatal(err)
    }
    if err := os.WriteFile("report-draft.pdf", data, 0o644); err != nil {
        log.Fatal(err)
    }
}

draft-stamp.png 은 빨간 굵은 "DRAFT" 글씨에 배경이 완전히 투명한 RGBA PNG 라고 가정한다. 본문 위에 올려지면 모든 투명 픽셀이 아래 단락을 비치게 한다. draft-stamp.png 을 어떤 로고, 인장, 서명 이미지로 바꿔도 된다 — 같은 코드 경로, 같은 SMask 처리가 돈다.

gpdf 가 PNG 에 실제로 하는 일

흥미로운 건 writer 쪽이다. PDF 에는 "RGBA 이미지" 라는 단일 객체가 없다. RGB 이미지 객체와 그에 대응하는 그레이스케일 SMask (소프트 마스크) 이미지 가 짝을 이루고, 마스크의 각 픽셀 값이 메인 이미지의 알파로 사용된다. 합성은 PDF 리더가 렌더 시점에 한다.

PNG 를 gpdf 에 넘기면 렌더러 (document/render/pdftarget.go) 가 픽셀 격자를 한 번 훑는다:

  • 24 비트 RGB 가 메인 이미지 스트림에 들어가고 FlateDecode 로 압축된다
  • 8 비트 알파가 별도의 SMask 스트림에 들어가고 역시 FlateDecode 된다
  • 이미지 사전에 /SMask <ref> 가 추가되어 알파 스트림을 참조한다

모든 알파 샘플이 0xFF (완전 불투명) 으로 끝나면 gpdf 는 알파 버퍼를 버리고 SMask 작성을 건너뛴다. JPEG 같은 불투명 PNG 는 결과물에서 추가 비용이 0 이라는 뜻. 비용은 알파가 실제로 일을 할 때만 든다.

이 경로 전체가 pure Go 다 — 표준 라이브러리의 image/png 가 디코드를, compress/flate 가 압축을 맡는다. CGO 도, libpng 의존성도 없다. macOS 에서 linux/arm64 (Lambda 등) 으로 크로스컴파일해도 결과는 여전히 정적 바이너리 한 개다.

JPEG 함정

"투명한" 로고가 어떤 도구에서 JPEG 로 내보내졌다면, 투명도는 gpdf 가 그 파일을 보기 전에 이미 사라졌다. JPEG 는 알파 채널을 가질 수 없으므로, 내보낸 도구가 적당한 배경색 (보통 흰색) 으로 알파를 평탄화한 상태다.

c.Image(jpegBytes) 자체는 잘 동작하지만, 임베드된 이미지는 원래 투명이던 자리에 불투명한 흰색 (혹은 검정, 분홍) 사각형을 남긴다. 수정은 상류에서 — PNG 로 다시 내보내야 한다. gpdf 에는 JPEG 에서 사라진 투명도를 복원하는 옵션이 없다.

"PNG-8" 의 팔레트 투명도는 별개 이슈다. gpdf 는 표준 image/png 를 쓰므로 팔레트 PNG 도 정상적으로 처리한다. 문제는 자산 파이프라인 어딘가에서 실수로 JPEG 를 거치는 경우 — 한 번 잃어버린 데이터는 돌아오지 않는다.

크기 조정과 워터마크

실용적인 확장은 두 가지로 충분하다.

로고 스케일링: template.FitWidth(document.Mm(40)) 또는 template.FitHeight(document.Mm(20)) 을 넘긴다. PNG 는 풀 해상도로 디코드된 뒤, 렌더링 시점에 PDF 좌표 변환으로 축소된다 — 알파에는 리샘플링 단계가 없다. 가장자리는 변함없이 또렷하다.

대각선 "DRAFT" 워터마크: 워터마크를 알파가 옅은 (25–40% 정도) PNG 로 만들고, 위 예제처럼 page.Absolute 로 배치한다. 알파가 픽셀 단위이므로 워터마크 안에서 불투명도를 변화시키는 것도 가능하다 — 그라디언트 페이드, 로고 실선 주변만 반투명, 같은 식. PDF 리더가 아래 텍스트와 알아서 합성한다.

픽셀 정확도로 30% 불투명도 오버레이가 필요하면 그건 이미지 편집기에서 알파를 굽는 결정이다. gpdf 는 받은 알파 값을 그대로 재현할 뿐이고, Builder API 에 이미지 단위 불투명도 곱셈 옵션은 없다.

파일 크기 감각

알파 있는 PNG → RGB 스트림 + 그레이스케일 SMask 스트림이므로 알파 없는 같은 이미지 대비 대략 33% 더 크다. 100 KB 의 불투명 PNG 임베드는 알파를 붙이면 약 133 KB 가 된다. 로고 한 장이라면 체감되지 않는다. 50 페이지 보고서에 페이지마다 워터마크를 깔아도 마찬가지 — SMask 는 한 번 등록되어 각 페이지에서 참조될 뿐 복제되지 않는다.

이미지 한 장이 갑자기 메가바이트 단위가 된다면 원인은 gpdf 의 인코딩이 아니라 원본 PNG 다. 임베드 전에 pngquantoxipng 에 한 번 통과시키자. 알파 채널은 두 도구 모두에서 살아남는다.

관련 레시피

gpdf 사용해 보기

gpdf 는 Go 의 PDF 생성 라이브러리다. MIT, 의존성 0, PNG 와 TrueType 모두 pure Go 처리.

go get github.com/gpdf-dev/gpdf

⭐ Star on GitHub · Read the docs