Bir PDF açıklaması, sayfaya iliştirilmiş bir sözlüktür; sayfanın üstüne çizilmiş bir iz değil. ISO 32000-1 §12.5 kabaca yirmi kadar alt tür tanımlar ve her biri bir /Subtype, sayfa koordinatlarında bir dikdörtgen, bir bayrak kümesi ve genellikle bir görüntüleyicinin gerçekte ne çizeceğine karar veren bir görünüm akışı taşır. Alt türlerin hepsi bir belgeyi inceleyen kişi için aynı anlama gelmez. Bir Highlight ve bir Ink darbesi yorumdur; bir Link gezinmedir; bir Popup ise yapışkan nota tıkladığınızda açılan küçük penceredir ve kendi nesnesi olarak saklanıp bir üst nesne tarafından işaret edilir. Yanıtlar, cevapladıkları yoruma bir in-reply-to girdisi üzerinden başvuran tam Text açıklamalarıdır. Yani sayfa düzeyindeki açıklama dizisi inceleyicinin yorum listesi değildir. Yorumları, onları birbirine bağlayan tesisatı ve hiçbir inceleyicinin yorum demeyeceği birkaç şeyi barındıran düz bir torbadır. Diziyi yorum listesi sayan bir panel, müşterinin kullandığı diğer her görüntüleyiciyle anlaşmazlığa düşer
Delphi, C++Builder ve Lazarus için PDFium tabanlı VCL/LCL bileşeni olan PDFium Component üzerinde bir açıklama inceleme akışı kurmak, ham dizi ile insan görünümü arasındaki bu boşluğun sorun çıkardığı noktalara odaklanmak demektir: saymak, dizinlemek, motorun çoktan dondurduğu izleri yeniden renklendirmek, hayalet bırakmadan silmek ve kendi işaretlerinizi eklemek
Sayınız neden Acrobat'ın yorum panelindekiyle asla uyuşmaz
İşaretlenmiş bir sözleşmeyi kendi görüntüleyicinizde ve Acrobat'ta yan yana açın; toplamlar nadiren uyuşur. Acrobat seçilmiş bir görünüm gösterir: işaretlemeler yanıt dizilerine gruplanmış, popup pencereleri ait oldukları notların içine katlanmış, bağlantılar ve form pencere ögeleri dışarıda bırakılmıştır. Ham dizi bunların hepsini ayrım yapmadan tutar; dolayısıyla naif bir sayım aynı anda bazı yönlerden yüksek, bazı yönlerden düşük çıkar
Popup pencereleri toplamı şişirir; çünkü her yapışkan not ayrı bir Popup nesnesiyle gelir ve ikisini birden saymak notu ikiye katlar. Görünür işaretlere göre süzerseniz yanıtlar toplamı düşürür; çünkü bir yanıt, biri diziyi genişletene dek hiçbir şey çizilmeyen bir Text açıklamasıdır ve onu atmak tartışmayı yitirir. Hidden ve NoView bayrakları bir açıklamayı ekrandan alır ama diziden almaz; bu yüzden bayrağa kör bir sayım kullanıcının göremediği işaretleri içerir. Link açıklamaları yorumlarla aynı dizide durur ve ne sayıma ne de listeye aittir. Sayma kuralına döngüyü yazmadan önce karar verin ve kararı yazıya dökün; çünkü "paneliniz neden Acrobat'tan farklı bir sayı gösteriyor" bir inceleme özelliğinin kazandığı ilk destek kaydıdır
Her şeyi bir kez dizinleyin, sonra bir sayfayı asla yeniden ayrıştırmayın
Sonraki her şeyi bir tasarım kuralı sürer: yazara, türe ya da sayfaya göre süzme, sayfa nesnelerini asla yeniden ayrıştırmamalıdır. Yoğun işaretlemeli 300 sayfalık bir belgede her açılır liste değişiminde yeniden ayrıştırmak, paneli saniyelerce takılan bir şeye çevirir. Bileşen, ikisi de o an yüklü sayfayla sınırlı olan AnnotationCount ile dizinli Annotation[] özelliğini sunar ve geri verdikleri TPdfAnnotation kaydı bir liste görünümünün ihtiyaç duyduğunu taşır: Subtype, Flags, Color, Rectangle, ContentsText, AuthorText. Doğru hamle, açılış anında her sayfayı bir kez taramak ve kendi düz dizininizi tutmaktır:
procedure TReviewPanel.BuildIndex;
var
PageNo, i: Integer;
A: TPdfAnnotation;
begin
FItems.Clear;
for PageNo := 1 to Pdf.PageCount do
begin
Pdf.PageNumber := PageNo;
for i := 0 to Pdf.AnnotationCount - 1 do
begin
A := Pdf.Annotation[i];
// Yalnızca inceleyiciyi ilgilendiren alt türleri tut; sayfa ve
// dizin çiftini kaydet, çünkü sonraki tüm düzenlemeler onunla adreslenir
if A.Subtype in [anText, anHighlight, anInk] then
FItems.Add(TReviewItem.Create(PageNo, i,
A.AuthorText, A.ContentsText, A.Rectangle, A.Color));
end;
end;
end;
Altını çizmeye değer çift (PageNo, i) ikilisidir. Sonraki her değişiklik, ister yeniden renklendirme ister silme olsun, sayfa numarası artı açıklama dizini ile adreslenir ve bu dizin kırılgandır: bir açıklamayı kaldırmak o sayfada ondan sonraki her şeyi yeniden numaralandırır. Bu yüzden dizin numaralarını yerinde yamamak yerine, herhangi bir silmeden sonra etkilenen sayfanın girdilerini yeniden kurmayı planlayın. Yeniden kurma bir milisaniyeye mal olur. Bayat bir dizin ise buna karşılık yanlış inceleyicinin yorumunu siler; bu da özelliğin tamamına duyulan güveni aşındıran türden bir hatadır
İlk sürümünüz yanıtları göstermek yerine yalnızca sayıyor olsa bile dizide iş parçacığı yapısına yer ayırın. Sayfa açıkken ögeleri üst başvurularına göre gruplayın ki panel sonradan bir diziyi Acrobat'ın yaptığı gibi katlayabilsin. O gruplamayı kaydırma sırasında tembel biçimde yeniden kurmak, bir kez dizinlemenin bütün amacını boşa çıkarır; çünkü ayrıştırma bedelini zaten ödediğiniz sayfaları yeniden açar. Geometri de aynı disiplini ister. Her kayıttaki Rectangle sayfa uzayındadır ve onu görünüm koordinatlarına çevirmek koda serpiştirilmiş değil tek bir paylaşılan yardımcıda olmalıdır. Seçim, isabet sınaması ve çizim işlerinin her biri kendi yakınlaştırma ve döndürme matematiğini uydurduğunda panellerde koordinat hataları biter; üçünü de tek bir dönüşümden geçirin, o zaman bir vurgu, listedeki satırı ve tıklama hedefi aynı mürekkebe sabitlenmiş kalır
İşaretlemeyi yeniden renklendirme ve görünüm akışı vetosu
Bir vurguyu sarıdan kehribara çevirmek tek satırlık bir iş gibi görünür ve bazen öyledir. Püf noktası ISO 32000-1 §12.5.5 maddesidir. Bir açıklama bir /AP görünüm akışı taşıdığında, uyumlu bir görüntüleyici o önceden kurulmuş akışı çizer ve sözlükteki renk girdisini ölü meta veri sayar. Acrobat oluşturduğu neredeyse her şey için görünüm akışı yazar; dolayısıyla müşterilerden gelen açıklamaların çoğu zaten bu durumdadır ve öyle güvenle ayarladığınız renk ekrana hiç ulaşmaz. Yeniden renklendirme, Annotation[] özelliği üzerinden bir oku, değiştir, yaz işlemidir ve bileşen çatışma konusunda dürüsttür: motor bir sözlük renginin pişmiş bir görünümü geçersiz kılmasına izin vermediğinde yazma işlemi EPdfError fırlatır
A := Pdf.Annotation[Item.Index];
A.HasColor := True;
A.Color := $0000B0FF; // kehribar
A.ColorAlpha := 160;
try
Pdf.Annotation[Item.Index] := A;
except
on EPdfError do
begin
// Açıklamanın önceden işlenmiş bir /AP akışı var; sözlükteki
// renk tek başına görüntüleyicilerin çizdiğini değiştiremez
Item.AppearanceLocked := True;
StatusBar.SimpleText := 'Color is fixed by the annotation appearance';
end;
end;
O istisnayı her seferinde yakalayın ve onu başarısızlık değil bilgi sayın. Korumayı atlarsanız paneliniz kendi listesinde neşeyle kehribar gösterirken sayfa sarıyı çizmeye devam eder; kullanıcı bunu haftalar sonra "görüntüleyiciniz düzenlemelerimi yok sayıyor" diye bildirir ve siz de bir öğleden sonrayı, tesadüfen görünüm akışı bulunmayan bir dosyada sorunu yeniden üretmeye çalışarak harcarsınız. Görünümün kilitli olduğunu öğrendikten sonra iki dürüst yanıtınız vardır: açıklama yerine kendi seçim kaplamanızı yeniden renklendirin ki inceleyici en azından seçtiği vurguyu görsün, ya da satırı görünüm kilitli olarak işaretleyin ki kimse değişikliğin kalıcı olmasını beklemesin
Açıklamaları hayalet bırakmadan silmek
DeleteAnnotation nesneyi geçerli sayfanın açıklama ağacından kaldırır ama önbelleklenmiş sayfa rasterine dokunmaz. Çağrının hemen ardından çizin, silinen vurgu hâlâ ekranda, artık arkasındaki belge modeliyle uyuşmayan bir bit eşlemde durur. Çözüm, yeniden işlemeyi çağıranın unutabileceği bir adım değil silmenin parçası saymaktır:
Pdf.PageNumber := Item.PageNo;
Pdf.DeleteAnnotation(Item.Index); // başarısızlıkta EPdfError fırlatır
Bmp := Pdf.RenderPage(0, 0, ViewWidth, ViewHeight, ro0, [reAnnotations]);
try
PaintPageBitmap(Bmp);
finally
Bmp.Free; // RenderPage bit eşlem sahipliğini çağırana devreder
end;
RebuildPageEntries(Item.PageNo); // Item.Index sonrasındaki dizinler kaydı
O blokta yanlış yapılması kolay iki ayrıntı var. reAnnotations seçeneğinin bulunması gerekir; yoksa yeni raster kalan her açıklamayı düşürür ve sayfa, tek bir işareti değil bütün yorum kümesini silmişsiniz gibi görünür. Bmp.Free de isteğe bağlı değildir: işlev biçimindeki RenderPage aşırı yüklemesi bit eşlem sahipliğini çağırana devreder; dolayısıyla eksik bir serbest bırakma her silmede tam sayfalık bir raster sızdırır ve uzun bir belgede çalışan bir inceleyici bunu dakikalar içinde gerçek bir bellek baskısına çevirir
Kendi arayüzünüzden inceleyici işaretleri eklemek
Açıklama oluşturma, doldurulmuş bir TPdfAnnotation kaydını (alt tür, dikdörtgen, renk, içerik, yazar) alıp geçerli sayfaya iliştiren CreateAnnotation üzerinden yürür. Alt türü anText olan bir yapışkan not kolay durumdur: konumu, içeriği ve yazarı ayarlarsınız, iş biter. İnsanların takıldığı yer Ink açıklamalarıdır. Kaydın dikdörtgeni yalnızca çizimi sınırlar; darbelerin kendisi, motorun mürekkep darbesi çağrısı FPDFAnnot_AddInkStroke ile FS_POINTF verisi beslenerek ayrıca iliştirilmesi gereken nokta dizileridir ve fare ya da kalem girdisinden her seferinde bir darbe olarak yakalanır. Bir Ink açıklamasını yalnızca bir dikdörtgenden kurarsanız boş alan olarak işlenen boş bir karalama elde edersiniz; bu, motorda bir hata gibi görünür ama aslında yarım kalmış bir açıklamadır
Yazarlık politikasını da aynı nefeste karara bağlayın. Arayüzünüzün oluşturduğu her işaret tutarlı bir AuthorText taşımalıdır; çünkü gelecek ay kuracağınız inceleyici süzgeci ancak bugün yorumlara bastığınız adlar kadar iyidir. Boş ya da tutarsız yazar dizgeleri, her dosyayı yeniden açmadan geriye dönük olarak onarılamaz
İncelemeyi görüntüleyicinin dışına çıkarmak
İnceleme verisi, görüntüleyiciyi terk edebildiği anda yerini hak eder: proje sorumlusunun dosyayı açmadan okuduğu bir özet ya da bir takip sayfasını besleyen bir CSV olarak. Yeni bir ayrıştırmadan değil, zaten kurduğunuz dizinden dışa aktarın ve her işarete geri başvurmak için kararlı bir yol seçin. Sayfa numarası ile açıklamanın dikdörtgeninden oluşan çift, bir dizi dizininin atlatamayacağı gidiş dönüşleri atlatır; çünkü bir sonraki silme dizinleri sessizce yeniden numaralandırır ve CSV dosyanız yanlış yorumları göstermeye başlar
Saklamaya değer bir satır sayfayı, alt türü, yazarı, dosyanın kaydettiği durumlarda oluşturma zaman damgasını, içerik metnini ve PDF'in sağladığı değil sizin sahip olduğunuz bir durum sütununu taşır. Aynı dizinleme geçişi daha erken, girişte de işe yarar: bir belge ekip dışından geldiğinde ve kimse incelemeden önce içinde ne olduğunu bilmek istediğinizde. PDF giriş tezgâhı yazısı o triyajı adım adım anlatıyor, form alanı gezinmesi ise ayna görüntüsü sorunu ele alıyor: yorum değil veri toplamak için kurulmuş belgeleri incelemek
Dizinin size göstermeyeceği bir durum
Bir başarısızlık kipi işaretlenmeyi hak ediyor; çünkü kodunuzda bir kusur gibi görünür ama değildir. Bir müşteri sayfanın her yerinde görünür vurgular bildirir ama paneliniz hiçbir şey listelemez ve AnnotationCount sıfır döner. Olağan açıklama, işaretlerin yukarı akışta bir yerde düzleştirilmiş olmasıdır. Düzleştirme, açıklama görünümlerini sıradan sayfa içeriğine pişirir; böylece vurgular sayfa grafiklerinin parçası olur ve açıklama nesnesi olarak var olmayı tümüyle bırakır. Bir açıklama API arayüzünün sayacağı, yeniden renklendireceği ya da sileceği hiçbir şey kalmaz. Çizilmiş işaretleme ile sıfır sayıyı bir arada gördüğünüzde hatayı sayma döngünüzde aramayı bırakın ve dosyanın nasıl üretildiğini sorun
Burada kullanılan açıklama yüzeyi, sayma ve oluşturmadan yeniden renklendirmeye, silmeye ve görüntüyü dürüst tutan işleme seçeneklerine kadar, Delphi, C++Builder ve Lazarus/FPC için PDFium Component ile birlikte gelir