Teknik Makale

Bir PDF'i Metin Düzenleyicide Elle Düzenleyin, Sonra Onarın

HotPDF, yüklenmiş bir belgeyi bir insanın okuyup düzenlemesi için tasarlanmış tanılama biçiminde kaydedebilir ve siz dosyayı düzenledikten sonra onu onarabilir. SaveLoadedDocumentDiagnostic, belgeyi belirlenimci (deterministic) sözlük sırasıyla, her nesneyi normal bir dolaylı nesne olarak, nesne akışı olmadan, çapraz referans akışı olmadan ve doğrusallaştırma olmadan yazar. RepairDiagnosticDocument ise ardından düzenlemenizin geçersiz kıldığı bayt ofsetlerini, akış uzunluklarını, çapraz referans tablosunu ve treyleri yeniden oluşturur

Bu ikili tek bir iş akışı için var: bir PDF'nin içinde gerçekte ne olduğunu anlamak veya düzeltmek. Bir render sorununu hata ayıklamış her geliştirici, dosyayı açıp bir sözlükteki bir sayıyı değiştirip sonucuna bakmak istemiştir. Normal bir PDF'de bu imkansızdır, çünkü içerik nesne akışlarına sıkıştırılmıştır ve eklediğiniz her bayt, bir çapraz referans tablosunun tam olarak kaydettiği ofsetleri kaydırır

Bir Tanılama Kaydını Farklı Kılan Nedir?

Altı ayar birlikte değişir. Sözlük anahtarları belirlenimci sırayla yazılır, bu yüzden aynı belgenin iki kaydı karşılaştırılabilir dosyalar üretir. Tanılama çıktı modu yapıyı açıklar. Tam yeniden yazma zorunlu kılınır, bu yüzden hiçbir şey dokunulmamış artımlı bir revizyon olarak taşınmaz. Doğrusallaştırma kapalıdır, çünkü doğrusallaştırılmış bir dosya bir okuyucu için değil bir web görüntüleyicisi için düzenlenmiştir. Çapraz referans akışları kapalıdır, bu yüzden tablo düz metindir. Ve nesne akışları kapalıdır, bu yüzden hiçbir nesne sıkıştırılmış bir kapsayıcının içinde gizlenmez

Sonuç, başlığı olağan %PDF- imzasının yanında bir tanılama işareti taşıyan ve gövdesinde herhangi bir düzenleyicide sayfa sayfa gezinebileceğiniz bir dosyadır. Nesneler art arda görünür, sözlükler okunaklıdır ve içerik akışları bulabileceğiniz yerlerde durur. Onarım aracı bu işareti kontrol eder ve onu taşımayan dosyaları reddeder, çünkü normal bir PDF'nin kompakt yapısı bir bayt aralığı onarıcısının yorumlamaya çalışması gereken bir şey değildir

Sıradan bir üretim PDF kaydını, düz okunabilir nesneler ve herhangi bir metin düzenleyicide açabileceğiniz düz metin bir xref tablosu yazan bir HotPDF tanılama kaydıyla karşılaştıran diyagram
Sıradan bir kayıt nesneleri sıkıştırılmış akışların içinde gizlerken tanılama yazıcısı her nesneyi düz metin bir çapraz referans tablosuyla blok başına bir tane olacak şekilde serer. Çıktı, herhangi bir düzenleyicide sayfa sayfa gezinip değiştirebileceğiniz bir dosyadır
uses
  HPDFDoc;

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('invoice.pdf') <= 0 then
      Exit;
    // Okunabilir, blok başına bir nesne, nesne akışlarında hiçbir şey gizli değil
    Pdf.SaveLoadedDocumentDiagnostic('invoice-diagnostic.pdf');
  finally
    Pdf.Free;
  end;
end;

Düzenleme Dosyayı Neden Bozar ve Ne Yeniden Oluşturulmalıdır?

Bir PDF'de bayt konumlarını veya bayt sayılarını kodlayan üç şey vardır. Çapraz referans tablosu her nesne numarasını bir bayt ofsetine eşler. Dosyanın sonundaki startxref değeri tabloyu işaret eder. Ve her akış sözlüğü, akış gövdesinin kaç bayt kapladığını söyleyen bir /Length taşır

Dosyanın sonundan önce herhangi bir yerde bir karakteri değiştirin, ondan sonraki ofsetler yanlış olur. Bir içerik akışına bir satır ekleyin, onun /Length değeri de yanlış olur. Çoğu görüntüleyici dosyayı reddeder ve onu kurtaranlar bunu tarama yaparak yapar; bu da hatayı düzeltmek yerine gizler. Onarım, bu üçünü de baytların şu anda gerçekte söylediklerinden yeniden oluşturur

Bir elle yapılan düzenlemenin bayt ofsetlerini kaydırdığını ve RepairDiagnosticDocument bu üçünü gerçek düzenden yeniden oluşturana kadar çapraz referans tablosunun, startxref işaretçisinin ve akış uzunluklarının bayatladığını gösteren diyagram
Bayt eklemek, sonrasında gelen her ofseti kaydırır, bu yüzden kaydedilen konumlar, startxref işaretçisi ve bildirilen akış uzunlukları gerçeklikle eşleşmeyi bırakır. Onarım her birini, düzenlenmiş dosyanın şu anda gerçekte içerdiklerinden yeniden oluşturur
var
  Options: THPDFDiagnosticRepairOptions;
  Report: THPDFDiagnosticRepairReport;
begin
  Options := THPDFDiagnosticRepairOptions.Default;
  Options.VerifyOutput := True;         // değiştirmeden önce sonucu yükle

  if THotPDF.RepairDiagnosticDocument('invoice-diagnostic.pdf',
       'invoice-fixed.pdf', Options, Report) then
    Writeln(Format('%d objects, %d streams, %d lengths corrected, ' +
      '%d xref rows rebuilt, %d pages verified',
      [Report.ObjectCount, Report.StreamCount,
       Report.RepairedStreamLengthCount, Report.RebuiltXRefEntryCount,
       Report.VerifiedPageCount]))
  else
    Writeln(Format('repair failed (%d): %s',
      [Ord(Report.Status), string(Report.Diagnostic)]));
end;

Baytları Kopyalayın, Yeniden Seri Hale Getirmeyin

Bu aracı güvenilir kılan tasarım kararı, belgeyi hiçbir zaman yükleyip geri yazmamasıdır. Doğruladığı nesnelerin bayt aralıklarını kopyalar, yalnızca yanlış olan /Length değerlerini değiştirir ve tabloyu, treyleri ve startxref'i bunların etrafında yeniden oluşturur

Yükleyip yeniden seri hale getirmek uygulamak çok daha kolay olurdu ve sessizce şeyleri değiştirirdi. Bilinmeyen nesne sözdizimi normalize edilir, dize kaçış dizileri yazıcının tercih ettiği biçimde yeniden yazılır ve zaten kodlanmış akış yükleri yeniden kodlanabilirdi. Bir hata ayıklama aracı için bu kabul edilemez: bir sayıyı düzenlediniz ve diff bin adet tesadüfi biçimlendirme değişikliği değil, bir sayı göstermelidir. İki dosya arasında tam olarak neyin değiştiğini bilmek istiyorsanız bu, iki PDF dosyasını yapı ve piksel bazında karşılaştırma başlığında ele alınan bir karşılaştırma sorunudur

HotPDF onarımının doğrulanmış bayt aralıklarını harfiyen kopyalamasını, sözdizimini normalize eden, dize kaçış dizilerini yeniden yazan ve akış yüklerini yeniden kodlayan bir yükle-ve-yeniden-seri-hale-getir yoluyla karşılaştıran diyagram
Onarım, doğrulanmış aralıkları bayt bayt kopyalar ve yalnızca yanlış olan /Length değerlerini yamalar, bu yüzden elle yaptığınız diff bozulmadan kalır. Yeniden yükleyip yeniden seri hale getirmek, hiç dokunmadığınız sözdizimini, kaçış dizilerini ve kodlamaları sessizce yeniden yazardı

Sahte Nesneleri Önleyen İki Ayrıştırma Kuralı

Bir bayt aralığı tarayıcısı bariz bir tuzakla karşı karşıyadır: bir nesne başlığına benzeyen metin, bir akış yükünün veya bir dizenin içinde görünebilir. Naif bir tarama, bir içerik akışının ortasını işaret eden bir çapraz referans satırı üretir ve sonuçtaki dosya yeni ve daha kafa karıştırıcı bir şekilde bozuk olurdu

HotPDF, doğrulanmış her nesnenin kaydedilen bitiş ofsetini kullanarak tüm bayt aralığını atlayarak bunu önler, bu yüzden bir nesne gövdesinin içindeki hiçbir şey aday olarak değerlendirilmez. Nesneler arasında yalnızca PDF boşluk karakterlerine ve yorumlara izin verilir; başka her şey, bölgenin anlaşılmadığı ve baytlarının çıktıya çekilmemesi gerektiği anlamına gelir

Akış uzunlukları da eşit özen gerektirir. Gerçek yük uzunluğu, bir yazıcının endstream'den önce eklediği satır sonu sınırlayıcısını hariç tutar. Bildirilen uzunluk tam olarak işaretçiye denk geldiğinde bu bildirim kabul edilir. Denk gelmediğinde onarıcı, endstream ve endobj çifti için sınırlı bir arama yapar ve tam olarak bir sınırlayıcıyı, bir CR, bir LF veya bir CRLF'yi çıkarır. Bunu bir bayt yanlış yapmak, açılan ve içerik akışının sonunda başıboş bir operatör bulunan bir sayfayı render eden bir dosya üretir

Treylerin Saklamasına İzin Verilenler

Yeniden oluşturulan treyler eskisinin bir kopyası değildir. /Size, /Prev ve /XRefStm hepsi önceki dosya düzenini tanımlar ve yeniden hesaplanır veya bırakılır. Yalnızca hedefleri doğrulanmış girdiler hayatta kalır: /Info, /ID ve /Encrypt. Ve /Root, tam olarak bir Catalog nesnesine çözümlenmelidir — sıfır değil, birkaç tane de değil; bu da nesne bloklarının kopyala-yapıştır yoluyla bir araya getirilmesiyle oluşturulan bir dosyanın üretme eğiliminde olduğu şeydir

Çıktı güvenliği, insanların önemsediği dosyalara karşı çalışan bir araç için olması gerektiği gibi ele alınır. Sonuç, hedef dizinde geçici bir dosyaya yazılır, ardından çapraz referans kurtarma devre dışı bırakılmış normal yol üzerinden yüklenerek doğrulanır, bu yüzden yalnızca kurtarma sayesinde açılan bir dosya başarısızlık sayılır. Sayfa ağacı doğrulandıktan sonra ancak o zaman geçici dosya hedefin yerini atomik olarak alır. Başarısız bir onarım hiçbir zaman mevcut bir çıktının üzerine yazmaz

Bütçeler ve Dürüst Başarısızlık Kodları

Üç sınır işi sınırlar: giriş baytları, aday nesne sayısı ve varsayılan olarak 8 GiB, iki milyon ve iki milyon olan maksimum nesne numarası. Nesne numarası üst sınırı göründüğünden daha önemlidir, çünkü milyarlarca numaralandırılmış birkaç nesneye sahip seyrek bir tablo, aksi halde yeniden oluşturulan çapraz referans tablosunu muazzam şekilde şişirirdi. Klasik çapraz referans tabloları ayrıca on ondalık basamaktan fazlasına ihtiyaç duyacak ofsetleri reddeder; bu bir uygulama tercihi değil bir biçim sınırıdır

Başarısızlıklar bir boolean yerine belirli bir durum bildirir: geçersiz argümanlar, kaynak çok büyük, tanılama dosyası değil, nesne sınırı aşıldı, geçersiz nesne, yinelenen nesne, eksik katalog, geçersiz treyler, çıktı hatası veya doğrulama başarısız. Her biri farklı bir düzeltmeye karşılık gelir. Yinelenen bir nesne, bir nesne bloğunu iki kez yapıştırdığınız anlamına gelir. Eksik bir katalog genellikle /Type /Catalog tutan nesnenin hasar gördüğü anlamına gelir. Doğrulama başarısız, yapının artık tutarlı olduğu ama sayfa ağacının yüklenmediği anlamına gelir; bu genellikle /Count'uyla uyumsuz şekilde düzenlenmiş bir /Kids dizisidir

İki pratik not döngüyü kapatır. Yalnızca onarılmış olanı değil, tanılama dosyasını da saklayın; böylece bir sonraki düzenleme okunabilir bir temelden başlar. Ve tanılama çıktısını son kullanıcılara göndermeyin: hiçbir şey nesne akışlarına sıkıştırılmadığı için normal bir kayıttan daha büyüktür ve kompakt bir dosya için üretim yolu, nesne akışları ve artımlı güncellemeler başlığında anlatılan sıradan kayıt yoludur. Amaç bir şeyi değiştirmek değil neyin yanlış olduğunu bulmaksa, preflight rapor otomasyonu başlığındaki otomatik yol, baytları okumaktan daha hızlı yanıt verir

Tanılama kaydı, onarım, preflight ve karşılaştırma, Delphi ve C++Builder'da aynı yüklenmiş belge modeli üzerinde çalışır; eksiksiz özellik listesi HotPDF Delphi PDF bileşeni sayfasında bulunur