PDF 처리 엔진 내부
PDF 처리 엔진 내부
당신이 일상적인 PDF 처리를 위해 사용하는 Document, Page, 및 Annotation 클래스는 하위 수준 aspose_pdf.engine 패키지에 대한 파사드입니다. 엔진은 실제 PDF 메커니즘을 구현합니다: 모든 PDF 파일이 구축되는 COS (Carousel Object Structure) 객체 모델, COS 객체와 PDF 바이트 사이를 변환하는 파서와 라이터, 주석 외관 합성, 페이지 래스터화, 글꼴 및 이미지 코덱 내부, 그리고 문서 암호화 및 디지털 서명을 위한 암호 원시 연산. 대부분의 애플리케이션은 aspose_pdf.engine를 직접 임포트할 필요가 없습니다 — 그러나 맞춤형 도구, 포렌식 PDF 검사, 혹은 고수준 API이 노출하지 않는 동작을 필요로 할 때 올바른 장소입니다.
단일 주석에 대한 외관 생성
사각형, 원형, 스탬프와 같은 인터랙티브 주석은 자동으로 일반 외관 스트림 (/AP /N)을 포함하지 않습니다. Annotation.generate_appearance를 호출하면 엔진의 외관 합성 내부 로직이 주석의 속성으로부터 필요에 따라 외관을 생성합니다.
from aspose_pdf import Document
doc = Document()
doc.pages.add()
ann = doc.pages[0].annotations.add(
"Square", (100, 100, 200, 200), "", properties={"C": [1, 0, 0], "IC": [0, 1, 0]}
)
print(ann.has_appearance) # False -- no appearance stream yet
ann.generate_appearance()
print(ann.has_appearance) # True -- the engine synthesised one
print(b"1 0 0 RG" in ann.appearance_normal) # True -- red stroke operator
print(b"0 1 0 rg" in ann.appearance_normal) # True -- green fill operator페이지와 문서 전체에 걸친 외관 일괄 생성
AnnotationCollection.generate_appearances는 한 번의 호출로 페이지 내 모든 해당 주석에 대한 외관을 합성하며, 엔진이 렌더링 방법을 모르는 하위 유형(예: Text)은 건너뜁니다:
from aspose_pdf import Document
doc = Document()
doc.pages.add()
page = doc.pages[0]
page.annotations.add("Square", (0, 0, 50, 50), "")
page.annotations.add("Circle", (60, 0, 110, 50), "")
page.annotations.add("Text", (0, 60, 20, 80), "") # unsupported subtype -> skipped
print(page.annotations.generate_appearances()) # 2Document.generate_appearances는 문서의 모든 페이지에 대해 동일하게 수행하며, 멱등성을 가집니다 — 외관이 이미 존재하면 두 번째 호출은 아무 작업도 수행하지 않습니다:
from aspose_pdf import Document
doc = Document()
doc.pages.add()
doc.pages.add()
doc.pages[0].annotations.add("Square", (0, 0, 50, 50), "")
doc.pages[1].annotations.add(
"Line", (0, 0, 50, 50), "", properties={"L": [0, 0, 50, 50]}
)
print(doc.generate_appearances()) # 2 -- one per page
print(doc.generate_appearances()) # 0 -- already generated, no-op주석을 정적 페이지 콘텐츠로 평탄화하기
Document.flatten()는 모든 주석의 외관을 페이지의 콘텐츠 스트림에 직접 그립니다(Do XObject 호출으로) 그리고 주석 객체 자체를 제거합니다. 따라서 주석을 완전히 무시하는 뷰어에서도 페이지가 동일하게 렌더링됩니다:
from aspose_pdf import Document
doc = Document()
doc.pages.add()
doc.pages[0].annotations.add(
"Square", (100, 100, 200, 200), "", properties={"C": [0, 0, 0]}
)
doc.flatten()이 호출 이후 페이지의 콘텐츠 스트림은 이전보다 길어지며(이제 인라인된 사각형을 포함합니다) doc.pages[0].annotations는 더 이상 평탄화된 주석을 포함하지 않습니다.
비밀번호 기반 암호화 키 파생 (Revision 4 / AES-128)
EncryptionUtils는 PDF 표준 보안 핸들러의 키 파생 및 AES-CBC 기본 연산을 직접 구현하며, Document API와는 무관합니다. 이는 맞춤형 도구나 암호화된 PDF에 대한 포렌식 검사에 유용합니다:
import os
from aspose_pdf.engine.encryption import EncryptionUtils
file_id = os.urandom(16)
user_pwd = "mypassword"
# Derive the owner (O) and user (U) key material for Revision 4 (128-bit AES)
o_value = EncryptionUtils.compute_owner_key_v4("owner", user_pwd, 16, 4)
u_value, enc_key = EncryptionUtils.compute_user_key_v4(
user_pwd, o_value, -4, file_id, 16, 4
)
# Encrypt data with the derived file-encryption key
plaintext = b"Confidential PDF content"
ciphertext = EncryptionUtils.encrypt_aes_cbc(enc_key, plaintext)
# Re-derive the key from the password before trusting it to decrypt
verified_key = EncryptionUtils.verify_password_v4(
user_pwd, u_value, o_value, -4, file_id, 16, 4
)
print(verified_key is not None) # True -- password matches
decrypted = EncryptionUtils.decrypt_aes_cbc(verified_key, ciphertext)
print(decrypted == plaintext) # TrueAES-CBC를 사용한 원시 콘텐츠 암호화
낮은 수준의 요구에 대해, EncryptionUtils.encrypt_aes_cbc()와 decrypt_aes_cbc()는 비밀번호 기반 키 파생 과정을 전혀 거치지 않고 16, 24, 또는 32바이트 키에 직접 작동합니다:
import os
from aspose_pdf.engine.encryption import EncryptionUtils
key = os.urandom(32) # AES-256; 16 and 24-byte keys are also accepted
plaintext = b"Hello, PDF AES 256!"
ciphertext = EncryptionUtils.encrypt_aes_cbc(key, plaintext)
decrypted = EncryptionUtils.decrypt_aes_cbc(key, ciphertext)
print(decrypted == plaintext) # True팁 및 모범 사례
- 일상적인 문서 처리를 위해 고수준
Document,Page,Annotation파사드를 사용하는 것이 좋습니다.aspose_pdf.engine패키지는 해당 클래스들이 기반을 두고 있는 내부 구현이며 — 맞춤 도구, 포렌식 검사, 또는 파사드가 노출하지 않는 동작이 필요할 때만 사용하십시오. revision인자를 대상 보안 핸들러에 맞추세요:compute_owner_key_v4/compute_user_key_v4는 리비전 2–4(40비트 및 128비트 RC4/AES)를 지원하고,compute_hash_v5은 AES-256에서 사용되는 리비전 5/6 알고리즘을 구현합니다. 리비전과 키 길이를 섞으면 조용히 잘못된 키가 생성됩니다.Document.generate_appearances와AnnotationCollection.generate_appearances은 멱등합니다 — 스스로 만든 것이 아닌 문서를 렌더링하거나 플래튼하기 전에 방어적으로 호출하세요.Document.flatten()은 파괴적입니다: 페이지 내용에 그린 모든 주석을 제거합니다. 다른 주석 편집을 먼저 마치거나 복사본에서 작업하세요.- 모든 주석 하위 유형에 내장된 외관 합성기가 있는 것은 아닙니다 —
Text와Popup이 흔한 예입니다.generate_appearance()을 호출한 후has_appearance를 확인하고 성공했다고 가정하지 마세요.
일반적인 문제
| 문제 | 원인 | 해결책 |
|---|---|---|
EncryptionUtils.encrypt_aes_cbc/decrypt_aes_cbc은(는) “AES key must be 16, 24, or 32 bytes” 오류를 발생시킵니다 | 잘못된 길이의 키가 제공되었습니다. | 키를 os.urandom(16), os.urandom(24), 또는 os.urandom(32)로 생성하십시오. |
EncryptionUtils.verify_password_v4은(는) 오류를 발생시키는 대신 None을(를) 반환합니다. | 제공된 비밀번호가 문서에서 파생된 U/O 값과 일치하지 않습니다. | 결과를 decrypt_aes_cbc에 전달하기 전에 None를 명시적으로 확인하십시오. |
Annotation.generate_appearance는 False를 반환합니다. | 주석의 하위 유형에는 내장된 외관 합성기가 없습니다 (예: Text 또는 Popup). | 직접 appearance_normal 바이트를 제공하거나 뷰어의 기본 렌더링을 수락하십시오. |
Document.generate_appearances에 대한 두 번째 호출은 0를 반환합니다. | 이 호출은 멱등합니다 — 이미 has_appearance == True가 있는 주석은 건너뛰어집니다. | 예상되는 동작이며, 오류가 아닙니다. |
FAQ
일상적인 문서 처리를 위해 aspose_pdf.engine에서 가져와야 하나요?
아니요. Document, Page, Annotation 클래스는 표준 문서 워크플로를 포괄합니다. 엔진 레이어는 해당 클래스들의 동작이 구현되는 곳이며, 맞춤 도구를 만들거나 PDF 내부를 직접 검사할 때 가장 유용합니다.
Annotation.generate_appearance와 AnnotationCollection.generate_appearances의 차이는 무엇인가요?
첫 번째는 단일 주석에 대한 appearance 스트림을 합성하고 bool를 반환합니다. 두 번째는 컬렉션(페이지의 주석 또는 Document.generate_appearances을 통해 문서의 모든 페이지) 내의 모든 해당 주석에 대해 동일하게 수행하고 생성한 appearance 수를 반환합니다.
키 파생 메서드가 revision 인수를 받는 이유는 무엇인가요?
PDF 표준 보안 핸들러는 ISO 32000 개정판을 거치며 진화해 왔습니다 — Revision 2는 40비트 RC4를 사용하고, Revision 3/4는 128비트 RC4 또는 AES를 지원하며, Revision 5/6(AES-256에 사용됨)은 완전히 다른 해싱 알고리즘(compute_hash_v5)을 사용합니다. revision 인수는 EncryptionUtils 메서드가 수행할 파생 방식을 선택합니다.
원시 PDF 객체를 직접 검사하거나 만들 수 있나요?
예. aspose_pdf.engine.cos는 COS 객체 모델 — PdfObject, PdfDictionary, PdfArray, PdfStream, PdfName 및 관련 타입 —을 노출하며, 이들 PdfCosWriter와 PdfCosParser는 PDF 바이트로 직렬화하고 파싱합니다.
페이지를 이미지로 렌더링하는 과정은 어디에서 이루어지나요?
Document.render_page은(는) RasterizedPage을(를) 반환합니다, 이는 렌더링된 페이지를 이미지 파일로 변환하기 위한 to_png(), to_tiff(), 및 save() 메서드를 가진 엔진 수준 객체입니다.
API Reference 요약
| 클래스 / 메서드 | 설명 |
|---|---|
Annotation.generate_appearance(force) -> bool | 요청 시 하나의 주석에 대한 일반 외관 스트림을 합성합니다 |
AnnotationCollection.generate_appearances(force) -> int | 페이지 내 모든 해당 주석에 대한 외관을 일괄 생성합니다 |
Document.generate_appearances(force) -> int | 문서 내 모든 해당 주석에 대한 외관을 일괄 생성합니다 |
Document.flatten() -> Document | 주석 외관을 페이지 콘텐츠에 인라인으로 삽입하고 주석을 제거합니다 |
Document.render_page(page_index, dpi, scale, background, antialias) -> RasterizedPage | 엔진의 렌더링 파이프라인을 통해 페이지를 래스터화합니다 |
RasterizedPage | 압축된 RGB 포맷의 렌더링된 페이지, to_png(), to_tiff(), 및 save()와 함께 |
GeneratedAppearance | 외관 합성의 내부 결과: 콘텐츠 바이트와 필요한 ExtGState/폰트 리소스 |
EncryptionUtils | AES-CBC/RC4 암호화 및 PDF 표준 보안 핸들러 키 파생(리비전 2–6) |
PdfObject | 모든 COS (Carousel Object Structure) 객체에 대한 추상 기본 클래스 |
PdfDictionary / PdfArray / PdfStream | 저수준 문서 트리를 구성하는 구체적인 COS 컨테이너 유형 |
PdfName / PdfNumber / PdfString / PdfBoolean / PdfNull | 원시 COS 값 유형 |
PdfIndirectReference | 다른 객체에 대한 COS 간접 참조(n g R) |
PdfCosWriter | 인메모리 COS PdfDocument를 PDF 바이트로 직렬화합니다 |
PdfCosParser / LazyPdfObjectStore | PDF 바이트를 COS 객체로 파싱하고, 필요에 따라 실체화합니다 |
IncrementalUpdate / IncrementalWriter | PDF를 다시 쓰는 대신 기존 PDF에 증분 업데이트 섹션을 추가합니다 |
SimplePdf | 고수준 Document API이 구축되는 native-Python 저수준 문서 표현 |
TextFragmentAbsorber / TextFragmentCollection | 저수준 텍스트 조각 추출을 SimplePdf 인스턴스에서 수행 |
ImagePlacementAbsorber / ImagePlacement | 페이지에 배치된 래스터 이미지를 찾고, 저장하고, 교체하거나 숨깁니다 |
SigningUtils | 디지털 서명을 위해 자체 서명 인증서와 PKCS#7/CAdES 서명을 생성합니다 |
DssMaterial / ChainResult / RevocationResult / TimestampInfo | 서명 검증 지원: DSS 자료, 인증서 체인 결과, 폐기 검사 및 RFC 3161 타임스탬프 검증 |
StandardFonts | 14가지 PDF 표준 글꼴에 대한 메트릭 및 인코딩 |
CidTextCodec | 복합 (Type0) 글꼴에 대한 show-strings를 인코딩하고 디코딩합니다 |
Shading | 축방향, 방사형 및 함수 기반 셰이딩에 걸친 RGB 색상 샘플링 |
Color / Matrix | 엔진 전반에 걸쳐 사용되는 저수준 색상 및 2-D 어핀 변환 프리미티브 |