Giải phẫu một tệp PDF
Tổng quan nhanh
Phần tiêu đề “Tổng quan nhanh”Mở bất kỳ PDF nào trong một trình soạn thảo văn bản thuần và thứ đầu tiên bạn
thấy khá yên tâm: một header %PDF-1.x hoặc %PDF-2.0. Thứ cuối cùng bạn thấy
là %%EOF. Mọi thứ nằm giữa hai dòng đó là một cỗ máy nhỏ gọn gàng để tìm các
đối tượng theo số. Trang này là một cuộc mổ xẻ. Chúng ta mở tệp ra, gọi tên từng
bộ phận, và cho thấy chúng kết nối với nhau như thế nào.
Đây là người bạn đồng hành về cấu trúc của hai trang lân cận. PDF thật sự là gì coi tệp như một đồ thị đối tượng; cập nhật tăng dần bàn về cách nó lớn lên theo thời gian. Trang này bám sát các byte — những vùng vật lý mà một trình phân tích cú pháp đi qua, theo đúng thứ tự chúng nằm trên đĩa.
Vì sao điều này quan trọng
Phần tiêu đề “Vì sao điều này quan trọng”Bạn gần như không bao giờ cần điều này để dùng một PDF. Bạn cần nó vào ngày một tệp gặp trục trặc. Một tệp mở được trong trình đọc này nhưng không mở được trong trình đọc khác; một trình kiểm định báo “bảng tham chiếu chéo hỏng”; một tài liệu đã ký bỗng nhiên không xác minh được. Không cái nào trong số đó còn là bí ẩn một khi bạn đọc được bản giải phẫu. Chúng là một con số không còn khớp với một vị trí, một vùng nằm sai chỗ, hay một phần đuôi trỏ vào hư không.
Biết bố cục biến “PDF bị hỏng” thành một chẩn đoán mà bạn có thể hành động. Đó là khác biệt giữa nhún vai trước một hộp đen và chỉ đúng vào byte đang nói dối.
Phiên bản ngắn gọn
Phần tiêu đề “Phiên bản ngắn gọn”Một PDF hợp quy có bốn phần vật lý, theo thứ tự trong tệp này (Spec: ISO 32000-2, §7.5.1ISO 32000-2 §7.5.1):
- Một header — một dòng,
%PDF-2.0, nêu phiên bản. - Một body — phần lớn của tệp: một chuỗi các đối tượng gián tiếp được đánh số.
- Một phần tham chiếu chéo — một chỉ mục từ số đối tượng tới byte offset nơi đối tượng đó nằm. PDF cổ điển dùng một bảng văn bản; PDF 2.0 dùng một luồng xref đã nén.
- Một trailer — một dictionary nhỏ nêu điểm vào, theo sau là
startxref, một offset, và%%EOF.
Điều bất ngờ: một trình đọc không bắt đầu từ trên cùng. Nó bắt đầu từ dưới
cùng, đọc startxref để tìm chỉ mục, và dùng chỉ mục đó để truy cập trực tiếp
bất kỳ đối tượng nào. Tệp được ghi từ đầu tới cuối nhưng được đọc từ cuối lên đầu.
Cách NextPDF tiếp cận việc này
Phần tiêu đề “Cách NextPDF tiếp cận việc này”Hãy đi qua bốn vùng theo thứ tự, với các byte ngay trước mặt.
Header là một dòng. NextPDF ghi %PDF-2.0, và theo quy ước thêm một dòng chú
thích thứ hai gồm các byte có bit cao để các công cụ truyền tệp ngây thơ coi tệp
là nhị phân, không phải văn bản. Dòng thứ hai đó là lý do một PDF mở dưới dạng văn
bản thuần hiển thị một chút nhiễu ngay sau phiên bản.
Body là nơi tài liệu sống. Mỗi đối tượng gián tiếp là một con số, một thế
hệ, từ khóa obj, một giá trị, và endobj
(Spec: ISO 32000-2, §7.3.10ISO 32000-2 §7.3.10). Giá trị là một trong vài hình
dạng — nhưng hai hình dạng gánh gần như toàn bộ trọng lượng:
- Một dictionary,
<< /Key value … >>, là một ánh xạ từ tên sang giá trị. Cây trang, catalog, các bộ mô tả font: tất cả đều là dictionary. - Một luồng là một dictionary theo sau bởi
stream, một khối byte tùy ý, vàendstream. Nội dung trang, font nhúng, và hình ảnh đều là luồng, gần như luôn được nén. (Các bộ lọc của chúng là một câu chuyện riêng, được kể trong luồng và bộ lọc.)
Các đối tượng trỏ tới nhau bằng tham chiếu gián tiếp — 2 0 R nghĩa là “đối
tượng 2, thế hệ 0.” Đó là phần dây nối biến một danh sách phẳng các đối tượng
thành một đồ thị.
Phần tham chiếu chéo là phần mà hầu hết mọi người chưa bao giờ hình dung đúng.
Ở dạng cổ điển nó là văn bản thuần: từ khóa xref, rồi các phân đoạn gồm các dòng
20 byte cố định chiều rộng (Spec: ISO 32000-2, §7.5.4ISO 32000-2 §7.5.4). Mỗi dòng là một byte offset mười chữ số, một thế hệ năm chữ
số, và một cờ duy nhất — n cho đang dùng, f cho trống — được đệm cho đúng hai
mươi byte để một trình đọc có thể nhảy tới bất kỳ mục nào chỉ bằng phép số học.
PDF 2.0 thay thế phần này bằng một luồng tham chiếu chéo: cùng chỉ mục đó,
nhưng nhị phân và được nén bên trong một đối tượng luồng đánh dấu /Type /XRef
(Spec: ISO 32000-2, §7.5.8ISO 32000-2 §7.5.8). Nhỏ hơn, và có khả năng mô tả
các đối tượng đóng gói bên trong các luồng đối tượng.
Trailer là mục lục của tệp (Spec: ISO
32000-2, §7.5.5ISO
32000-2 §7.5.5). Nó nêu /Root — document catalog, đối tượng duy
nhất mà mọi thứ khác treo vào — và /Size, số lượng đối tượng. Rồi đến cái bắt
tay khiến việc đọc ngược trở nên khả thi: startxref, một byte offset trên dòng
riêng, và %%EOF. Một trình đọc nhảy tới cuối, đọc offset đó, nhảy thẳng tới phần
tham chiếu chéo, và lên đường.
- HeaderOne line, %PDF-2.0, naming the version. A binary-marker comment usually follows.
- BodyNumbered indirect objects — dictionaries and streams — referenced by N G R.
- Cross-reference sectionA text table of 20-byte entries, or a compressed /Type /XRef stream in PDF 2.0.
- TrailerNames /Root and /Size, then startxref + offset + %%EOF.
- Read orderA reader starts at %%EOF, follows startxref to the index, then reaches each object directly.
Có một vùng thứ năm mà một tệp tồn tại lâu sẽ phát triển thêm: một bản cập nhật
tăng dần. Một thay đổi không được ghi tại chỗ. Các đối tượng bị thay đổi, một
phần tham chiếu chéo mới, và một trailer mới được nối thêm sau %%EOF đầu tiên,
và trailer mới đó mang /Prev — offset của phần tham chiếu chéo trước đó
(Spec: ISO 32000-2, §7.5.6ISO 32000-2 §7.5.6). Các phần tạo thành một chuỗi
lùi về sau; với bất kỳ số đối tượng nào, mục mới nhất sẽ thắng. Vì các byte gốc
không bao giờ di chuyển, phép kiểm tra mật mã trên dải byte mà một chữ ký thực sự
bao phủ vẫn còn đúng sau một bản cập nhật. Một bản cập nhật tăng dần về sau vẫn có
thể thay đổi những gì một trình kiểm định báo cáo về toàn bộ tài liệu — liệu các
thay đổi sau khi ký có được phép hay không, và chữ ký được hiểu là chứng nhận điều
gì — nhưng nó không thể làm thay đổi chính các byte đã được ký. Tính chất đó là cả
chủ đề của cập nhật tăng dần.
Ví dụ thực tế
Phần tiêu đề “Ví dụ thực tế”Đây là toàn bộ bản giải phẫu trong một tệp tối giản. Các con số dưới xref là
byte offset, và chúng phải chính xác — trỏ lệch một ký tự so với nơi một đối tượng
bắt đầu là một trình đọc nghiêm khắc sẽ bỏ cuộc.
%PDF-2.01 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>endobjxref0 40000000000 65535 f0000000009 00000 n0000000058 00000 n0000000115 00000 ntrailer<< /Size 4 /Root 1 0 R >>startxref186%%EOFĐọc nó theo cách một trình phân tích cú pháp đọc. Dòng cuối: %%EOF. Phía trên
nó: startxref 186, nên nhảy tới byte 186, nơi xref bắt đầu. Bảng nói đối tượng
1 nằm ở byte 9. /Root 1 0 R của trailer trỏ tới đó — catalog — và từ catalog bạn
đi theo /Pages tới cây trang và tìm thấy trang duy nhất. Đối tượng 0 luôn là đầu
của danh sách trống với thế hệ 65535, một di tích từ thiết kế đầu tiên của định
dạng mà mọi trình đọc vẫn còn trông đợi nhìn thấy.
Hiểu lầm thường gặp
Phần tiêu đề “Hiểu lầm thường gặp”Cái bẫy là đọc một PDF như một câu chuyện — từ trên xuống dưới, theo thứ tự. Nó
không phải là một câu chuyện; nó là một chỉ mục với một con trỏ lùi về sau. Các số
đối tượng không nhất thiết phải tuần tự trong tệp, các đối tượng có thể xuất hiện
theo bất kỳ thứ tự vật lý nào, và một trình đọc không bao giờ dựa vào vị trí của
chúng. Bản đồ có thẩm quyền duy nhất là phần tham chiếu chéo, và cách duy nhất để
tìm bản đồ đó là offset startxref ở chính cuối tệp.
Hệ quả khiến nhiều người bất ngờ. Một PDF có body hoàn hảo và một chữ số sai trong
startxref thì không đọc được — trình đọc không thể tìm thấy chỉ mục. Một PDF có
các đối tượng theo thứ tự lộn xộn nhưng có phần tham chiếu chéo đúng thì hoàn toàn
ổn. Vị trí vật lý không mang ý nghĩa nào. Vị trí được ghi lại mới mang tất cả ý
nghĩa.
Giới hạn và ranh giới
Phần tiêu đề “Giới hạn và ranh giới”Trang này mô tả cấu trúc vật lý, không phải nội dung trang. Cách các dấu vết hiện
lên trên một trang — các toán tử luồng nội dung, hiển thị văn bản, trạng thái đồ
họa — là một chủ đề riêng. Nó cũng mô tả một tệp định dạng đúng. Các PDF ngoài
đời thực thường hơi lỗi một chút và sống sót chỉ vì các trình đọc khoan dung dựng
lại bảng tham chiếu chéo bằng cách quét tìm các từ khóa obj. Việc cứu vãn đó là
một hành vi của trình đọc, không phải điều mà định dạng bảo đảm.
| Edition | Availability |
|---|---|
| Core | NextPDF là một bộ ghi. Nó ghi lại mọi offset từ buffer đầu ra vào đúng thời điểm mỗi đối tượng được phát ra, nên các tệp nó tạo ra có một phần tham chiếu chéo khớp với body theo cách dựng. |
| Pro | Phân tích cú pháp, tái dựng, hay sửa chữa một bảng tham chiếu chéo bị hỏng trong một tệp mà NextPDF không ghi nằm ngoài phạm vi trên mọi phiên bản. |
| Enterprise | Để kiểm tra cấu trúc của một tệp đã có, hãy dùng một trình phân tích cú pháp hoặc trình kiểm định chuyên dụng; NextPDF bảo đảm tính đúng đắn cho những gì nó ghi, không phải cho những gì nó đọc. |
Mini-FAQ
Phần tiêu đề “Mini-FAQ”Tại sao một PDF có một dòng đánh dấu nhị phân sau header? Một số công cụ truyền tệp cũ sẽ làm hỏng một tệp mà chúng tưởng là văn bản thuần. Dòng chú thích có bit cao khiến tệp trông rõ ràng là nhị phân, nên nó sống sót qua chuyến đi mà không bị thay đổi.
Luồng xref có phải chỉ là một bảng nhỏ hơn không? Hầu hết là vậy, với một sức mạnh thêm. Ngoài việc được nén, một luồng xref có thể mô tả các đối tượng được lưu bên trong các luồng đối tượng — những mục mà bảng văn bản 20 byte cổ điển không có cách nào diễn đạt.
Tôi có thể có cả một bảng lẫn một luồng trong một tệp không? Một bản sửa đổi đơn lẻ dùng một trong hai. Nhưng một tệp lai có thể ghép một bảng cổ điển cho các trình đọc cũ với một luồng tham chiếu chéo cho các trình đọc mới, để mỗi loại trình đọc tìm thấy một chỉ mục mà nó hiểu.
Tài liệu liên quan
Phần tiêu đề “Tài liệu liên quan”- PDF thật sự là gì — cùng bốn phần đó nhìn dưới dạng một đồ thị đối tượng thay vì một bố cục vật lý.
- Cập nhật tăng dần và vì sao chúng quan trọng — cách vùng thứ năm được nối thêm làm một tệp lớn lên và bảo vệ các chữ ký.
- Luồng và bộ lọc — những gì nằm bên trong các đối tượng luồng của body và cách chúng được nén.
- PDF 2.0: những gì đã thay đổi — vì sao luồng tham chiếu chéo là cấu trúc mặc định mà NextPDF ghi.
Bảng thuật ngữ
Phần tiêu đề “Bảng thuật ngữ”- Header — dòng đầu tiên,
%PDF-2.0, nêu phiên bản; thường theo sau bởi một chú thích đánh dấu nhị phân. - Đối tượng gián tiếp — một đối tượng được đánh số trong body, viết
N G obj … endobj, trong đóNlà số đối tượng vàGlà thế hệ. - Dictionary — một ánh xạ
<< /Key value … >>từ tên sang giá trị; hình dạng đối tượng phổ biến nhất. - Luồng — một dictionary cộng với một khối byte giữa
streamvàendstream, dùng cho nội dung, font, và hình ảnh. - Bảng tham chiếu chéo (xref) — chỉ mục từ số đối tượng sang byte offset; một
bảng văn bản 20 byte mỗi mục theo cách cổ điển, một luồng
/Type /XReftrong PDF 2.0. - Trailer — dictionary nêu
/Rootvà/Size, được tìm thấy qua offsetstartxrefở cuối tệp. - Cập nhật tăng dần — các đối tượng bị thay đổi, một phần tham chiếu chéo mới,
và một trailer mới được nối thêm sau
%%EOF, với/Prevliên kết chuỗi ngược về phần trước.