Python에서 PDF 데이터를 작업하는 방법
Aspose.PDF FOSS for Python는 PDF 문서의 XMP 메타데이터를 XmpPacket, XmpField, XmpArray, XmpStruct, XmpProperty 객체로 구성된 메모리 내 데이터 모델로 표현합니다. NamespaceProvider는 네임스페이스 접두사를 URI로 해석합니다. 이를 통해 RDF/XML을 직접 작성하지 않고도 구조화된 메타데이터—제목, 날짜, 키워드 목록, 사용자 정의 필드—를 읽고, 생성하고, 재작성할 수 있습니다. 이 라이브러리는 순수 Python이며 아래 명령어로 설치됩니다.
단계별 가이드
1단계: 패키지 설치
Aspose.PDF FOSS 패키지를 설치합니다:
git clone https://github.com/aspose-pdf-foss/Aspose-PDF-FOSS-for-Python.git
cd Aspose-PDF-FOSS-for-Python
pip install -e .XmpPacket를 임포트하고 확인 메시지를 출력하여 설치를 검증합니다:
from aspose_pdf import XmpPacket
print("aspose-pdf-foss-for-python is ready.")단계 2: 필요한 클래스 가져오기
XMP 데이터 모델 클래스와 네임스페이스 해결자를 가져옵니다:
from aspose_pdf import (
NamespaceProvider,
XmpArray,
XmpField,
XmpPacket,
XmpProperty,
XmpStruct,
)단계 3: 패킷을 생성하고 간단한 속성을 설정합니다
XmpPacket()는 빈 필드 목록으로 시작합니다. set_value(prefix, name, value, uri=...)은 간단한 속성을 설정하거나 교체하고, get(prefix_or_uri, name)는 이를 XmpField로 다시 읽어옵니다; get는 등록된 접두사 또는 전체 네임스페이스 URI 중 하나를 허용합니다:
from aspose_pdf import XmpPacket
packet = XmpPacket()
packet.set_value("dc", "title", "Quarterly Report")
packet.set_value(
"dc", "creator", "Automation Pipeline",
uri="http://purl.org/dc/elements/1.1/",
)
title_field = packet.get("dc", "title")
print(title_field.value) # "Quarterly Report"단계 4: 형식화된 값 저장
XmpPacket는 XMP가 내부적으로 저장하는 평문 형태와 상호 변환하는 형식화된 편리 접근자를 제공합니다: set_date/get_date, set_int/get_int, set_real/get_real, 및 set_bool/get_bool. 각 getter는 속성이 없을 때 None를 반환하며 예외를 발생시키지 않습니다:
from datetime import datetime
packet.set_date("xmp", "CreateDate", datetime(2026, 7, 29, 9, 30))
packet.set_int("pdf", "PageCount", 42)
packet.set_real("custom", "ConfidenceScore", 0.97, uri="https://example.com/ns/custom/1.0/")
packet.set_bool("custom", "IsFinal", True, uri="https://example.com/ns/custom/1.0/")
print(packet.get_date("xmp", "CreateDate"))
print(packet.get_int("pdf", "PageCount"))
print(packet.get_real("custom", "ConfidenceScore"))
print(packet.get_bool("custom", "IsFinal"))단계 5: 지역화된 텍스트 및 정렬된 배열 저장
set_localized_text는 특정 언어로 dc:title와 같은 언어 대체(rdf:Alt) 속성을 작성하며, 기본값은 "x-default"입니다. set_array는 값의 평범한 목록에서 정렬된(Seq), 정렬되지 않은(Bag), 또는 대체(Alt) 배열을 작성하고, get_array은 이를 문자열 목록으로 다시 읽어옵니다:
packet.set_localized_text("dc", "description", "Quarterly summary", lang="en")
packet.set_localized_text(
"dc", "description", "Resumen trimestral",
uri="http://purl.org/dc/elements/1.1/", lang="es",
)
print(packet.get_localized_text("dc", "description", lang="es"))
packet.set_array("dc", "subject", ["finance", "quarterly", "internal"], kind="Bag")
print(packet.get_array("dc", "subject"))단계 6: XmpStruct로 구조화된 값 만들기
일부 XMP 속성(차원 레코드, 히스토리 항목)은 단순 텍스트가 아니라 구조화된 값입니다. XmpStruct로 하나를 만들고, add와 함께 XmpField 객체를 추가하며, get으로 이름으로 멤버를 읽은 다음, add로 구조체를 패킷에 첨부합니다:
dimensions = XmpStruct()
dimensions.add(XmpField(prefix="stDim", name="w", value="612"))
dimensions.add(XmpField(prefix="stDim", name="h", value="792"))
dimensions.add(XmpField(prefix="stDim", name="unit", value="pt"))
print(dimensions.get("w").value) # "612"
packet.add(XmpField(prefix="xmpTPg", name="MaxPageSize", value=dimensions))단계 7: 사용자 정의 XMP 네임스페이스 등록
NamespaceProvider은 표준 XMP 네임스페이스(Dublin Core, Adobe XMP, PDF 등)와 함께 미리 로드됩니다. register(prefix, uri)을 호출하여 사용자 정의 매핑을 추가합니다 — 이 메서드는 제공자를 반환하므로 호출을 체인할 수 있습니다 — 그런 다음 제공자를 패킷에 첨부하여 직렬화 시 사용자 정의 접두사가 해당 URI로 해석되도록 합니다:
from aspose_pdf import NamespaceProvider, XmpPacket
provider = NamespaceProvider()
provider.register("custom", "https://example.com/ns/custom/1.0/")
packet = XmpPacket(namespace_provider=provider)
packet.set_value("custom", "batch_id", "run-2026-07-29")단계 8: 패킷 파싱 및 직렬화
XmpPacket.parse(data, provider=...)은 원시 XMP 패킷 바이트 또는 텍스트를 XmpPacket으로 읽어들입니다. serialize()와 to_bytes()은 모두 패킷을 XMP 패킷 바이트로 다시 렌더링합니다:
xmp_bytes = packet.to_bytes()
restored = XmpPacket.parse(xmp_bytes)
print(restored.get("dc", "title").value)일반적인 문제 및 해결 방법
get() 또는 타입이 지정된 getter가 반환합니다 None 내가 방금 속성을 설정했음에도
확인: prefix (또는 URI) 인수가 get/get_int/get_date/etc.에 전달된 것이 매칭되는 set_* 호출에 전달된 것과 정확히 일치하는지 확인하십시오 — get는 필드의 프리픽스와 네임스페이스 URI 모두를 매칭하지만, 한 호출에서는 맞춤 프리픽스가 다르고 다른 호출에서는 URI가 있으면 일치하지 않습니다.
사용자 정의 네임스페이스 프리픽스가 직렬화될 때 불완전한 출력을 생성합니다
표준 XMP 네임스페이스(dc, xmp, pdf 등)에 속하지 않는 프리픽스는 각 set_* 호출 시 명시적인 uri= 인수가 필요하거나, XmpPacket(namespace_provider=provider)을 통해 해당 프리픽스를 등록하고 패킷에 연결한 NamespaceProvider가 있어야 하며, 그 후에 serialize()/to_bytes()을 호출해야 합니다.
get_bool, get_int, 또는 get_real returns None 설정된 것으로 알고 있는 값에 대해
저장된 텍스트가 예상 타입으로 파싱되지 않을 때 이러한 타입 지정 getter는 None을 반환합니다 — 예를 들어, 값이 set_int이 아니라 자유 형식 텍스트로 set_value를 사용해 설정된 속성에서 get_int이 발생합니다. getter의 파싱 로직이 쓰기 경로와 일치하도록 일치하는 타입 지정 setter(set_int, set_real, set_bool)를 사용하십시오.
XmpPacket.parse raises ValueError 신뢰할 수 없는 소스에서 온 패킷에
parse는 XXE 및 billion-laughs 공격에 대한 적대적 입력 방어 차원에서 모든 <!DOCTYPE 또는 <!ENTITY 선언을 거부합니다. 정상적인 XMP 패킷은 DTD가 필요하지 않으며, 예외를 방어를 우회하기보다 소스 스트림이 손상되었거나 안전하지 않다는 신호로 간주하십시오.
get_array returns None 리스트 대신
get_array은(는) 지정된 속성이 존재하지 않거나 XmpArray로 저장되지 않은 경우 None을 반환합니다(예: set_array 대신 set_value으로 설정된 경우). 속성을 쓸 때는 set_array를 사용하여 저장된 값이 get_array이(가) 기대하는 형태와 일치하도록 하세요.
자주 묻는 질문
set_value과 set_int와 같은 타입 지정 세터의 차이점은 무엇인가요?
set_value은(는) 전달한 value을 그대로 저장합니다. 타입 지정 세터(set_date, set_int, set_real, set_bool)는 입력을 XMP가 내부적으로 사용하는 평문 형태로 변환하고, 이를 다시 파싱하는 대응하는 타입 지정 getter와 짝을 이루므로, 순수 문자열 대신 왕복 타입 안전성이 필요할 때 사용하십시오.
XmpArray 종류(Bag, Seq, Alt)는 어떻게 다릅니까?
Bag은(는) 순서가 없는 값 집합이며, Seq은(는) 순서가 있는 목록, Alt는 대체 값을 보관합니다(대부분 set_localized_text에서 사용되는 언어 대체값). 원하는 종류를 set_array(..., kind="Bag")에 전달하거나 직접 XmpArray(kind=...)를 생성하세요.
패킷의 속성 이름을 미리 알지 못해도 원시 속성 목록을 읽을 수 있나요?
예 — packet.fields를 반복합니다. 이는 패킷에 삽입 순서대로 추가된 모든 XmpField, XmpArray, XmpProperty를 보유합니다.
표준 XMP 네임스페이스에 NamespaceProvider가 필요합니까?
아니요. dc, xmp, pdf 및 기타 표준 접두사는 자동으로 해결됩니다. NamespaceProvider는 직접 만든 사용자 정의 접두사에만 필요합니다.
XmpProperty 한정자는 단순한 XmpField과 어떻게 다릅니까?
XmpProperty는 기본 XmpField을 add_qualifier로 추가된 한정자 필드 목록과 함께 감싸며, 이는 속성 자체에 값에 추가적인 RDF 메타데이터가 필요하고, XmpField.language가 이미 다루는 xml:lang 한정자를 넘어서는 드문 경우에 사용됩니다.