Anda memanggil AddText untuk mencap sebuah baris ke sebuah halaman PDF dengan PDFiumPas, lalu segera memanggil FindFirst untuk mengonfirmasi cap itu mendarat, dan pencarian itu kembali kosong. Teks itu ada di halaman — Acrobat menampilkannya — tetapi komponen TPdf milik PDFiumPas menyimpan sebuah struktur FPDF_TEXTPAGE ter-cache terpisah, di-parse sekali dari content stream halaman, dan sebuah edit tidak secara retroaktif memperbarui struktur itu dengan sendirinya. Query itu sebelum ia disegarkan dan Anda membaca halaman tersebut persis seperti tampilannya sebelum perubahan Anda, bukan sesudahnya
Mengapa PDFium mengembalikan teks basi tepat setelah sebuah edit?
PDFiumPas membungkus mesin rendering PDFium milik Google untuk Delphi dan C++Builder, dan pemanggilan teks dan editing-nya menjangkau dua subsistem berbeda di dalam mesin itu. FPDF_TEXTPAGE adalah milik sisi baca: FPDFText_LoadPage menelusuri content stream halaman sekali dan membangun text page — kode karakter, posisi, metrik font, batas kata — dan PDFiumPas menjaga struktur itu tetap ter-cache selama halaman itu tetap dimuat. Pemanggilan editing seperti FPDFPage_InsertObject atau FPDFPage_GenerateContent beroperasi pada representasi yang sama sekali berbeda, object graph dan content-stream milik halaman, dan PDFium tidak mendorong perubahan itu ke dalam sebuah text page yang sudah terbuka dengan sendirinya. Membangun ulangnya pada setiap edit akan membuat batch editing tidak bisa diterima lambatnya, sehingga desain ini menukar biaya itu dengan sebuah aturan sebagai gantinya — siapa pun yang memegang handle menutupnya setelah sebuah edit pengubah-konten, dan pembacaan berikutnya membangun yang baru
Di dalam cache teks TPdf: FTextPage, LoadTextPage, dan UnloadTextPage
TPdf melacak handle ter-cache dalam satu field privat tunggal, FTextPage, dan membungkus siklus hidupnya dalam dua metode. LoadTextPage memeriksa apakah FTextPage adalah nil dan, hanya dalam kasus itu, memanggil FPDFText_LoadPage terhadap halaman saat ini; jika sebuah handle sudah ada, LoadTextPage menggunakannya kembali tanpa bertanya apakah halaman itu berubah sejak dibangun. UnloadTextPage adalah separuh lainnya: ia menutup handle native dengan FPDFText_ClosePage, mengatur FTextPage kembali ke nil, dan juga membuang daftar web-link ter-cache dan sesi find apa pun yang sedang berlangsung, karena keduanya diturunkan dari text page yang sama dan menjadi basi karena alasan yang sama
Perilaku penggunaan-ulang-tanpa-pemeriksaan milik LoadTextPage adalah persis mengapa urutan itu penting. Setiap query teks pada TPdf — Text, FindFirst, GetWebLinks — menyalur lewat LoadTextPage lebih dulu, sehingga selama FTextPage masih memegang handle pra-edit, tak satu pun dari pemanggilan itu punya cara mengetahui sebuah perubahan terjadi. Navigasi halaman tidak pernah menjadi risiko di sini: UnloadPage, yang berjalan pada pergantian halaman, reload, dan penutupan dokumen, selalu menutup text page beserta halaman itu sendiri. Pertanyaan terbuka selalu tentang edit yang diterapkan pada halaman tempat Anda masih duduk
Metode PDFiumPas mana yang menyegarkan cache secara otomatis?
Metode pengeditan-halaman milik TPdf sendiri — AddText, SetText, SetTextPositions, AddPath, RemoveObject, dan InsertFormObjectFromXObject — masing-masing memanggil UnloadTextPage sebelum memanggil UpdatePage (FPDFPage_GenerateContent milik PDFium) untuk menyerialkan perubahan tersebut ke dalam content stream. Panggil salah satu dari ini dan pemanggilan Text, FindFirst, atau GetWebLinks berikutnya membangun ulang text page dari konten sebagaimana adanya sekarang, tanpa pemanggilan ekstra yang dibutuhkan di pihak Anda
var
Pdf: TPdf;
Index: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'invoice.pdf';
Pdf.Active := True;
Pdf.PageNumber := 1;
Pdf.AddText('Reviewed by J. Alvarez', 'Helvetica', 10, 72, 40, clBlack, 255, 0);
// AddText already closed the cached text page, so this FindFirst
// call rebuilds it fresh before it searches
Index := Pdf.FindFirst('Reviewed by J. Alvarez');
if Index >= 0 then
ShowMessage('Stamp confirmed at character ' + IntToStr(Index));
finally
Pdf.Free;
end;
end;
Pola yang tetap rusak: meng-cache handle TextPage mentah
TPdf mengekspos handle hidupnya lewat sebuah properti read-only TextPage, untuk kasus langka di mana Anda perlu memanggil sebuah fungsi FPDFText_* yang belum dibungkus PDFiumPas. Pintu darurat itu juga satu-satunya tempat invalidasi otomatis tidak bisa membantu: begitu Anda menyalin nilai FPDF_TEXTPAGE keluar dari properti itu ke dalam sebuah variabel lokal, PDFiumPas tidak memiliki cara mengetahui Anda masih memegangnya, dan tidak ada cara memperbarui salinan Anda ketika UnloadTextPage berjalan di tempat lain dalam kode Anda
var
Pdf: TPdf;
RawHandle: FPDF_TEXTPAGE;
StaleCount: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'contract.pdf';
Pdf.Active := True;
Pdf.PageNumber := 1;
RawHandle := Pdf.TextPage; // FPDFText_LoadPage handle, cached in FTextPage
Pdf.SetText(0, 'Amended Clause 4.2');
// SetText already closed RawHandle and set Pdf.TextPage back to nil.
// Calling any FPDFText_* function against the old value now touches a
// handle PDFium has already freed — undefined behavior, not a bug you
// can catch with a nil check
StaleCount := FPDFText_CountChars(RawHandle);
finally
Pdf.Free;
end;
end;
Menggunakan sebuah handle setelah FPDFText_ClosePage berjalan padanya adalah undefined behavior di PDFium itu sendiri, bukan sebuah konvensi PDFiumPas yang bisa Anda pilih untuk diabaikan — ia mungkin mengembalikan data terakhir-diketahui, tidak mengembalikan apa pun, atau meng-crash proses tersebut, dan mana dari itu yang terjadi pada sebuah build tertentu bukan sesuatu yang seharusnya diandalkan kode aplikasi. Aturan amannya sempit: baca Pdf.TextPage baru, tepat sebelum pemanggilan FPDFText_* yang membutuhkannya, dan jangan pernah memegang sebuah salinan melewati sebuah statement yang mungkin mengedit halaman tersebut
Batch edit Anda, lalu query sekali
Tak satu pun dari ini berarti setiap pemanggilan AddText atau RemoveObject membutuhkan sebuah query teks defensif tepat setelahnya untuk memeriksa hasilnya. Setiap metode editing sudah membayar biaya menutup text page sekali; melakukan query setelah setiap edit tunggal di dalam sebuah loop membayar biaya itu lagi tanpa manfaat, karena FPDFText_LoadPage menelusuri ulang seluruh content stream setiap kali ia berjalan
var
Pdf: TPdf;
I: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'watermarked.pdf';
Pdf.Active := True;
Pdf.PageNumber := 1;
// Strip every text object that looks like a draft watermark. Each
// RemoveObject call already invalidates the cache on its own, so
// nothing needs refreshing by hand between iterations
for I := Pdf.ObjectCount - 1 downto 0 do
if (Pdf.ObjectType[I] = otText) and (Pdf.ObjectBounds[I].Top > 700) then
Pdf.RemoveObject(I, True);
// Query once, after the whole batch is done, not once per removal
if Pdf.FindFirst('DRAFT') < 0 then
ShowMessage('Watermark cleared');
finally
Pdf.Free;
end;
end;
Logika batching yang sama berlaku khusus pada state pencarian. FindNext dan FindPrevious melanjutkan sebuah sesi yang dimulai FindFirst, dan sesi itu dibongkar UnloadTextPage beserta segala sesuatu lainnya, sehingga memanggil FindNext lagi setelah sebuah edit — alih-alih memanggil FindFirst lagi — memunculkan sebuah exception alih-alih diam-diam melanjutkan sebuah pencarian terhadap konten yang tidak lagi ada. Perlakukan edit apa pun sebagai sebuah batas keras baik untuk konten teks maupun posisi pencarian, dan biarkan satu FindFirst baru di sisi jauh edit Anda mengambil kembali pencarian tersebut
Di mana ini cocok dengan pekerjaan ekstraksi dan anotasi
Ekstraksi teks polos — membaca teks sebuah halaman tanpa mengubah apa pun — tidak pernah menabrak apa pun dari ini, karena tidak ada yang membatalkan sebuah handle yang tidak disentuh edit apa pun. Untuk bagaimana Text, rectangle karakter, dan batas kata bekerja pada sebuah halaman yang tidak dimodifikasi, artikel pendamping tentang mengekstrak teks dengan PDFiumPas membahas dasar itu tanpa siklus hidup cache text page yang ditambahkan artikel ini di atasnya
Siklus hidup cache ini paling penting dalam workflow yang mengedit dan kemudian langsung bertindak atas hasilnya: mencap sebuah koreksi dan mencarinya, meredaksi sebuah paragraf dan mengonfirmasi ia sudah hilang, atau menemukan sebuah frasa untuk menjangkarkan sebuah anotasi markup tepat setelah menyisipkan teks di dekatnya. Kasus terakhir itu layak ditandai sendiri — anotasi markup quad-point diposisikan dari rectangle karakter yang dibaca dari text page, sehingga sebuah anotasi yang dibangun dari koordinat yang ditangkap sebelum sebuah edit berakhir menyorot tempat yang salah begitu edit itu mendarat
API editing dan teks milik TPdf adalah bagian dari PDFium Component untuk Delphi dan C++Builder, dan halaman produk membawa referensi metode lengkap untuk permukaan editing, ekstraksi, dan pencarian yang dibahas di sini