Внутреннее устройство PDF Processing Engine
Внутреннее устройство PDF-движка
Document, Page и Annotation классы, которые вы используете для повседневной обработки PDF, являются фасадом над более низкоуровневым пакетом aspose_pdf.engine. Движок реализует настоящую механику PDF: модель объектов COS (Carousel Object Structure), из которой построен каждый PDF-файл, парсер и писатель, преобразующие объекты 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 больше не содержит уплощённую аннотацию.
Получение ключей шифрования на основе пароля (Ревизия 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) # TrueШифрование необработанного содержимого с помощью AES-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реализует алгоритм версии 5/6, используемый AES-256. Смешивание версий и длины ключа без предупреждения приводит к неверному ключу. Document.generate_appearancesиAnnotationCollection.generate_appearancesявляются идемпотентными— вызывайте их предосторожно перед рендерингом или уплощением документа, который вы не создавали.Document.flatten()разрушителен: он удаляет каждую аннотацию, которую вставляет в содержимое страницы. Завершите редактирование остальных аннотаций сначала или работайте с копией.- Не каждый подтип аннотации имеет встроенный синтезатор внешнего вида—
TextиPopupявляются распространёнными примерами. Проверьтеhas_appearanceпосле вызоваgenerate_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 | Явно проверьте наличие None перед передачей результата в decrypt_aes_cbc |
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?
Первый синтезирует поток отображения для одной аннотации и возвращает bool. Второй делает то же самое для каждой подходящей аннотации в коллекции (аннотации страницы или, через Document.generate_appearances, каждую страницу в документе) и возвращает количество созданных отображений.
Почему методы вывода ключа принимают аргумент 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/font |
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 вместо его полной перезаписи |
SimplePdf | Нативное Python низкоуровневое представление документа, на котором построен высокоуровневый Document API |
TextFragmentAbsorber / TextFragmentCollection | Низкоуровневое извлечение фрагментов текста над экземпляром SimplePdf |
ImagePlacementAbsorber / ImagePlacement | Найдите, сохраните, замените или скройте растровые изображения, размещённые на странице |
SigningUtils | Создавайте самоподписанные сертификаты и подписи PKCS#7/CAdES для цифровой подписи |
DssMaterial / ChainResult / RevocationResult / TimestampInfo | Поддержка проверки подписи: материалы DSS, результаты цепочки сертификатов, проверки отзыва и проверка тайм-стампа RFC3161 |
StandardFonts | Метрики и кодировки для 14 стандартных шрифтов PDF |
CidTextCodec | Кодировать и декодировать show-strings для составных (Type0) шрифтов |
Shading | Пример RGB-цвета для осевых, радиальных и основанных на функции затенений |
Color / Matrix | Низкоуровневые примитивы цвета и 2-D аффинных преобразований, используемые по всему движку |