- Nếu muốn thay đổi nội dung văn bản trong PDF mà không dùng công cụ PDF thương mại, bạn cần giải nén các stream đã nén, lần theo mã hóa theo từng phông chữ, rồi thay văn bản gốc và đoạn thay thế theo cùng một cách
- Nhiều PDF không lưu văn bản thực của tài liệu dưới dạng plain text, mà dưới dạng stream nén và các chuỗi trông giống hệ thập lục phân; có thể dùng
qpdf --qdf --object-streams=disable để giải nén sang dạng gần với có thể chỉnh sửa
- Các khối văn bản gồm những lệnh PDF như
BT, Tf, Td, Tj, ET, và các giá trị như <002a004800570003003600480057> Tj có thể không phải là các code point Unicode đơn giản
- Nếu PDF có ánh xạ ToUnicode theo từng phông chữ, bạn có thể dùng
pdffonts để kiểm tra emb và uni có phải yes hay không, rồi lần theo tham chiếu như /ToUnicode 19 0 R để tìm bảng mã hóa
- Do độ phức tạp của chuẩn PDF, cách này khó khái quát cho mọi tệp, nhưng với một số PDF cụ thể, bạn có thể tìm mã hóa của văn bản gốc và thay thế văn bản bằng find-and-replace
Giải nén stream văn bản PDF đã nén
- Khi mở PDF bằng trình soạn thảo văn bản, bạn có thể đọc được một phần cấu trúc, nhưng không thấy văn bản thực của tài liệu
- Nhiều PDF lưu dữ liệu văn bản ở dạng đã nén
- Có thể dùng
qpdf để giải nén và kiểm tra các stream văn bản đã nén
- Trên Mac có homebrew formula
- Trên các bản phân phối Linux cũng có thể dùng các công cụ liên quan
qpdf --qdf --object-streams=disable in.pdf out.pdf
- Sau khi chỉnh sửa, bạn có thể nén lại stream
qpdf out-edited.pdf out-recompressed.pdf
- Lệnh nén lại đã báo lỗi, nhưng tệp PDF kết quả vẫn đọc được trong Preview
Tìm văn bản bên trong các lệnh PDF
- Ngay cả sau khi giải nén, văn bản vẫn nằm trong các khối lệnh PDF
- Một khối văn bản cơ bản có dạng như sau
BT
/Font_0 12 Tf
288 720 Td
<002a004800570003003600480057> Tj
ET
- Theo PDF Reference, khối này thực hiện lần lượt các thao tác sau
- Bắt đầu một đối tượng văn bản
- Thiết lập phông chữ và kích thước sẽ dùng
- Chỉ định vị trí bắt đầu trên trang
- Vẽ glyph của chuỗi tại vị trí đó
- Kết thúc đối tượng văn bản
Vì sao văn bản không đọc được ngay
- Các giá trị như
<002a004800570003003600480057> trông giống chuỗi thập lục phân, nhưng có thể không phải là danh sách code point Unicode đơn giản
- PDF có nhiều cách chỉ định mã hóa, và có thể chứa mã hóa tùy chỉnh bên trong tệp
- Những mã hóa như vậy phần lớn có thể được ánh xạ sang code point Unicode
- Quy trình này giả định rằng PDF bạn đang xử lý có chứa mã hóa nhúng
- Không đề cập cách xử lý các trường hợp khác
Kiểm tra phông chữ và ánh xạ Unicode
- Mã hóa văn bản của PDF gắn với một phông chữ cụ thể
pdffonts hiển thị thông tin liên quan đến phông chữ và mã hóa bên trong PDF
$ pdffonts sample.pdf
name type emb sub uni prob object ID
------------------------------------ ----------------- --- --- --- ---- ---------
CLDQZB+TrebuchetMS,Bold CID TrueType yes yes yes 9 0
YQBAIZ+TrebuchetMS CID TrueType yes yes yes 10 0
- Các trường cần kiểm tra ở đây là
emb: nghĩa là phông chữ đã được nhúng vào PDF
uni: nghĩa là có ánh xạ Unicode
- Nếu cả hai giá trị đều là
yes, có khả năng tìm được ánh xạ cần thiết
- Các định danh phông chữ như
/Font_0 trong ví dụ có thể tương ứng với tên phông chữ trong kết quả của pdffonts, tùy trường hợp
- Trong trường hợp đó,
/Font_0 trỏ tới CLDQZB+TrebuchetMS,Bold
Lần theo bảng ánh xạ ToUnicode
- Sau khi tìm được tên đầy đủ của phông chữ dùng trong văn bản, hãy tìm tên đó bên trong PDF
- Ở một vị trí nhất định, bạn có thể tìm thấy tham chiếu như sau
/ToUnicode 19 0 R
- Giá trị này trỏ tới ID đối tượng của bảng mã hóa
- Sau đó, khi tìm
19 0 obj, có thể tìm được bảng tương ứng
Thay thế văn bản bằng bảng mã hóa
- Phần cốt lõi của bảng ánh xạ có thể có dạng như sau
38 beginbfrange^M
<0036><0036><0053>^M
<0057><0057><0074>^M
<0044><0044><0061>^M
<0048><0048><0065>^M
<0050><0050><006D>^M
...
- Ví dụ này ánh xạ phạm vi các điểm mã hóa tùy chỉnh sang các điểm Unicode
- Ở đây, phạm vi chỉ gồm một ký tự
0036 được ánh xạ sang điểm Unicode 0053
- Nếu định dạng bảng khác, có thể tham khảo ToUnicode mapping file tutorial
- Để chuyển đổi tự động, hãy dùng Python để biến bảng thành dictionary và viết các hàm mã hóa/giải mã đơn giản
- Luồng thay thế cuối cùng như sau
- Xác định giá trị mã hóa tùy chỉnh của văn bản hiện có cần thay
- Mã hóa văn bản thay thế theo cùng cách mã hóa tùy chỉnh
- Dùng find-and-replace trong PDF để thay giá trị cũ
1 bình luận
Ý kiến trên Hacker News
Đặc tả PDF khá là gồ ghề. Mẩu kiến thức lặt vặt mình thích nhất gần đây là khi render các phần tử chồng lên nhau, nó hỗ trợ tất cả chế độ hòa trộn layer của Photoshop
Điều mình thích thứ hai là nội dung được nối thêm phía sau có thể sửa đổi nội dung phía trước, nên về mặt pháp chứng phải xem xét tất cả các phiên bản khác nhau được biểu diễn trong cùng một tệp
Đây cũng là một ví dụ cho thấy sự phù phiếm của DRM. Đặc tả có mã hóa dựa trên mật khẩu, có thể đặt riêng mật khẩu “chủ sở hữu” và “người dùng”, đồng thời cũng có các trường bit như “cấm in”, “cấm sao chép văn bản”. Nhưng để đọc tài liệu thì cuối cùng vẫn phải giải mã, nên nếu mở PDF đã mã hóa bằng mật khẩu “người dùng” trong một công cụ không tuân thủ rồi lưu thành phiên bản không mã hóa, bạn sẽ có một bản tương đương có thể chỉnh sửa
[1] Mục “More than just transparency” của https://blog.adobe.com/en/publish/2022/01/31/20-years-of-tra...
[2] https://blog.didierstevens.com/2008/05/07/solving-a-little-p...
[3] Trang 61 của https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandard...
[4] Ví dụ như một script dùng thư viện pypdf
Một trong những hướng dẫn crack nổi tiếng thời đó là vá hàm kiểm tra quyền để gỡ DRM của trình đọc chính thức
Chủ đề này xuất hiện định kỳ; đa số nghĩ PDF là một định dạng nhị phân khó chọc vào, nhưng thực ra không phải vậy
PDF là một đồ thị đối tượng gồm nhiều kiểu khác nhau, và bản thân các kiểu được mô tả rõ trong đặc tả chính thức. Có một thú vui khổ sở là đọc nó cho vui
Điều tôi luôn khuyên là, giống tác giả, hãy chuyển PDF sang phiên bản không có dữ liệu nén. Công cụ ưa thích là mutool, dùng
mutool clean -d in.pdf out.pdflà được. Sau đó khi lục lọi, bạn sẽ ngạc nhiên vì có thể lần theo nhiều phần hơn tưởng tượngBài viết đã bỏ qua bước xem đối tượng trang và kiểm tra tài nguyên. Đó là nơi tên font dùng trong content stream được liên kết với đối tượng con thực tế
Một phần quan trọng khác bị bỏ sót là hầu hết font được nhúng vào PDF dưới dạng tập con. Chỉ các glyph cần thiết được giữ lại trong font. Việc tái mã hóa có vẻ thường diễn ra ở đây, còn ToUnicode được giữ để cho phép sao chép văn bản hoặc tìm kiếm trong PDF. Đây là thứ tốt cho người dùng nếu có; theo kinh nghiệm của tôi thì nhìn chung nó tồn tại và khá đúng
Ngay từ đầu, triết lý thiết kế muốn tạo ra một định dạng chỉ đọc đã sai rồi. Chẳng phải tính năng đầu tiên mọi người sẽ yêu cầu là quá rõ sao
Không biết có ai có góc nhìn nào khác về cách xử lý tốt theo kiểu native mà không cần nhà cung cấp cloud không. Đặc biệt là khi xử lý bảng
Có thể làm như sau:
Chuyển sang PostScript
a.psbằngpdf2ps a.pdfChỉnh sửa trực tiếp PostScript bằng
vim a.psChuyển lại sang PDF bằng
ps2pdf a.psVới các PDF phức tạp có JavaScript nhúng hay animation chẳng hạn, sau vòng chuyển đổi qua lại này đôi khi chúng không hoạt động đúng. Dù vậy, nếu là tài liệu bình thường thì cách này hoạt động khá tốt. Việc gỡ watermark, thay đổi từ ngữ và con số có thể làm dễ dàng, còn chỉnh khoảng cách thì khó hơn. Tất nhiên bạn cần biết PostScript ở một mức độ nào đó.
Dù đầu ra có đúng đi nữa, chúng tôi vẫn cần sắp xếp lại trang, áp dụng barcode cho máy xử lý thư và phân loại thư, rồi tạo báo cáo. Thường chúng tôi dùng Perl và các công cụ khác để cào văn bản từ trang nhằm lấy địa chỉ; PS nhìn chung dễ hơn PDF rất nhiều, nhưng cũng có những trường hợp cực kỳ mong manh, chẳng hạn khi PDF “bảo mật” không có ánh xạ glyph đúng
Trường hợp tệ nhất là khách hàng gửi các PDF tài liệu đơn lẻ, và khi gộp chúng lại thì số lượng font subset trong bản in tăng vọt, lấp đầy bộ nhớ máy in và làm nó chết. Khi tôi nghỉ việc đó, hình như vẫn chưa có công cụ hữu ích nào để hợp nhất/gộp các font subset xuất phát từ cùng một font gốc. Lẽ ra điều đó phải khả thi và sẽ rất hữu ích, nhưng tôi không có thời gian hay kiến thức để tìm hiểu
Thành thật mà nói, có vẻ như chỉ những người viết malware mới hưởng lợi từ độ phức tạp của PDF
Có vẻ như một điểm quan trọng đã bị bỏ sót. Ở cuối PDF có một bảng lưu byte offset của nhiều đối tượng trong file, tức bảng tham chiếu chéo
Nếu sửa bên trong file, các offset này thường sẽ thay đổi và làm hỏng file. Bài viết có vẻ chỉ quan tâm đến việc thay một chữ số bằng một chữ số khác, nên vị trí có thể đã không đổi
Nhưng nói chung, nếu thêm/xóa/sửa ở giữa file thì phải tính lại bảng xref, vì vậy dùng thư viện sẽ dễ hơn rất nhiều so với chỉnh trực tiếp bằng trình soạn thảo văn bản
qpdf dường như tái cấu trúc lại cấu trúc PDF gốc và cố giữ nguyên cả số hiệu object, nhưng các offset thì được tính lại hoàn toàn
Nếu một ngày nào đó các tác giả kể lại, tôi muốn đọc vì sao định dạng này lại thành ra như vậy. Có vẻ ban đầu họ định làm nó hoàn toàn ở dạng text, rồi sau đó quyết định binary hiệu quả hơn. Tuy nhiên, việc phải cố định độ dài các mục trong bảng xref cũng ăn khớp với giả định đó theo một cách hơi kỳ lạ
mutool cleanđể sửa. Vì nó có thể được suy ra hoàn toàn từ nội dung nên an toànMiễn là giữ nguyên độ dài, bạn có thể sửa binary tùy ý
Một phần mềm tôi từng dùng trước đây có xác thực với server, nhưng mọi xử lý đều diễn ra ở client. Kiểu như client chạy SQL trực tiếp trên server, còn server chỉ kiểm tra xem client này có vượt quá số license đã mua hay không
Tôi có thể đưa nó vào disassembler, tìm đoạn kiểm tra, đổi thẳng đoạn đó thành JMP rồi lấp phần trống còn lại bằng NOP
Có vẻ như một bước quan trọng bị thiếu khi dùng chế độ
--qdfcủa qpdf. Sau khi chỉnh sửa xong, cần cho file đi qua tiện ích fix-pdf để tính lại toàn bộ object offset và dựng lại bảng tham chiếu chéo ở cuối file. Ngoại lệ là nếu bạn chỉ thay đổi tại chỗ mà không thêm hay xóa byte nàoTop 3 sự thật thú vị về PDF là như sau
[0] Ví dụ: https://lab6.com/2
Nếu muốn học thêm trong hành trình với PDF, bạn có thể đọc tài liệu mổ xẻ file mà tôi mới tổng hợp gần đây:
https://pdfsyntax.dev/introduction_pdf_syntax.html
Điều mọi người thường bỏ qua về PDF là, ở một khía cạnh nào đó, nó gần với định dạng hình ảnh hơn là tài liệu Word. Trong biên tập tài liệu, tài liệu Word, PDF và hình ảnh giống như dự án DAW, MIDI và MP3 trong âm nhạc; cũng tương tự mã nguồn Java, bytecode JVM và mã máy x86 thuần trong phần mềm
Mục đích chính của tệp PDF là cho biết một cách hoàn toàn rõ ràng cần hiển thị hoặc in gì, với số byte ít hơn rất nhiều so với một hình ảnh thực. Nó tận dụng việc tác giả tài liệu biết các mẫu cấu trúc của tài liệu, để nếu biểu diễn đúng cách thì có thể nén tốt hơn nhiều so với các thuật toán nén ảnh thực sự. Ví dụ, nếu có thể truy cập phông chữ thật, thì thay vì đưa mọi lần xuất hiện của ký tự vào như một phần của ảnh rồi trông chờ thuật toán nén tự giảm phần lặp lại, tốt hơn là nói “đặt các ký tự này ở các tọa độ này với khoảng cách này”. Ký tự nào thuộc về từ nào, thậm chí codepoint Unicode nào tương ứng với glyph nào của phông chữ, về cơ bản không quan trọng nếu mục tiêu chỉ là truyền tải hình ảnh tài liệu một cách hiệu quả
Với một tài liệu có thể chỉnh sửa, không chỉ cách biểu diễn mà cấu trúc ngữ nghĩa của nội dung trở nên quan trọng hơn nhiều. Việc một chỗ ngắt cụ thể trong văn bản là nhiều dấu cách, là cột tiếp theo của bảng, hay là bố cục trang kỳ lạ do hình ảnh gây ra đều quan trọng. Việc phần văn bản ở cuối mỗi trang là do tác giả chèn nhiều lần, hay chỉ nhập một lần rồi đặt làm chân trang, cũng quan trọng. Nếu thêm một đoạn mới khiến bố cục trang phải thay đổi, thì việc đoạn cuối trang này là chú thích cuối trang nên không được tràn sang trang sau là điều quan trọng. Điều quan trọng là khi tiêu đề mục chuyển sang trang khác thì mục lục phải tự động cập nhật, chứ không được chỉ là văn bản do tác giả nhập thủ công
Với máy in hoặc màn hình thì những điều này chẳng liên quan gì. Chỉ cần in hoặc hiển thị theo chỉ thị là được. Trong PDF, chú thích cuối trang, tiêu đề mục, chân trang và mục lục không cần phải là thứ gì đặc biệt; chúng cũng có thể chỉ là văn bản được định dạng vô nghĩa. Vì vậy, nếu cố dùng PDF cho mục đích không phải hiển thị hay in ấn thì không thể đạt độ chính xác 100%. Dĩ nhiên có những nỗ lực để bù đắp điều này, và chương trình tạo PDF có thể tự do đưa metadata mong muốn vào, nhưng đó không phải là bắt buộc
Đây có thể không phải là mô hình tinh thần mà những người tạo ra PDF dự định, nhưng là một góc nhìn hữu ích để hiểu vì sao PDF có hình dạng như vậy
Khi in bằng máy in PostScript thì nó trông chính xác như nhau, và nếu dùng phông Type 1 thì đôi khi còn tốt hơn
Người muốn làm việc này đang bỏ lỡ điểm cốt lõi của PDF. PDF là định dạng mô tả trang, nên nó không biểu diễn cấu trúc tài liệu mà chỉ biểu diễn các dấu vết hiển thị trên trang
Đừng cố chỉnh sửa PDF; hãy chỉnh sửa tài liệu gốc đã tạo ra PDF
Không hiểu sao việc gửi thẳng tài liệu được tạo ra để chỉnh sửa lại trở thành chuyện “không chuyên nghiệp”, và cuối cùng ta rơi vào tình cảnh này
Đây là tùy chọn, nhưng được khuyến nghị vì khả năng tiếp cận. Ví dụ có PDF/UA. Xem các chương 14.7–14.8 của [1]
[1] https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandard...
Không thể trực tiếp thay đổi văn bản PDF. Vì người dàn trang sẽ không thấy thay đổi đó. Công cụ markup của Acrobat không đến nỗi tệ, nhưng không bằng thời dùng giấy và bút chì đỏ. Trừ khi dùng công cụ “pencil” của Acrobat. Tôi mong phần này được cải thiện
20 năm trước tôi từng làm phụ trách bản in ở một tòa soạn báo. Chúng tôi có hai “máy in” bản kẽm Kodak trị giá một triệu đô, nhưng gọi là máy in thì không chính xác. Nếu tôi nhớ đúng, nhũ tương trên bản kẽm bị chiếu tia cực tím rồi tan trong bể hóa chất
Thiết bị Kodak thường xuyên lỗi, và sếp tôi hay mở trực tiếp các tệp PostScript hoặc EPS để sửa phần header hoặc chỗ sai mà phần mềm dàn trang gửi ra. Sau đó mọi thứ lại chạy tốt. Nhân tiện, chiếc máy in offset cuộn khổng lồ của Đức của chúng tôi chạy Linux
Tôi nhớ sếp tên là Bill. Ông ấy là một người tốt, từng đưa tôi, khi đó 17 tuổi, đi xem buổi diễn của Sigur Ros. Thế là có tới hai chuyện chẳng liên quan gì đến PDF rồi
Bài viết hay. Khoảng 5 năm qua, khi xây dựng DocSpring [1], tôi đã đọc khá nhiều đặc tả PDF nhưng vẫn có cảm giác mới chỉ chạm nhẹ bề mặt. qpdf là một công cụ tuyệt vời, và một công cụ khác tôi thích là RUPS [2], cho phép đào sâu vào cấu trúc PDF
[1] https://docspring.com
[2] https://github.com/itext/i7j-rups