İnceleme sırasında bir paragrafın etrafına çizilen bir dikdörtgenin PDF içinde bir işarete dönüşmesi gerekmez. HotPDF'in THPDFViewerModel'i, her vurgulamayı yüklenmiş belgede bir değişiklik yerine bellek içi bir kayıt olarak tutan bir yöntem olan AddHighlightRegion'ı sunar; böylece bir inceleyici düzinelerce sayfayı işaretlerken diskteki dosya byte byte aynı kalır. %6400'e yakınlaştırın, sayfayı 90 derece döndürün, Genişliğe Sığdır'dan Sayfaya Sığdır'a geçin — aynı dikdörtgen yine aynı paragrafa iner, çünkü koordinat matematiği işaretin çizildiği andaki gerçek oluşturma geometrisi üzerinden çalışır
Bir PDF görüntüleyici etrafında inşa edilen inceleme araçları bu sorunla sürekli karşılaşır. Bir kırmızı çizgi ekranı, üretilen faturalar üzerinde bir QA geçişi, dahili bir onay iş akışı: bunların hepsi, birinin bir bölgeye dikkat çekmesine izin vermesi gerekir; her taslak işaretin dosyada kalıcı bir değişikliğe dönüşmesi olmadan ve işaretin ait olup olmadığına birisi hâlâ karar verirken renkli bir kutu göstermek için tam bir açıklama alt sistemine uzanmadan. HotPDF buna, Delphi'de MVC mimarisiyle özel bir PDF görüntüleyici inşa etme'de anlatılan ayrımın tamamen Model tarafında oturan özel bir vurgulama katmanıyla yanıt verir; aynı vurgulama listesinin görünürde hiçbir pencere tanıtıcısı olmadan bir birim testinden yönlendirilebilmesinin nedeni de budur
HotPDF'in AddHighlightRegion'ı gerçekte neyi saklar?
AddHighlightRegion, işaret başına tam olarak üç şey saklar: sıfır tabanlı bir sayfa dizini, PDF kullanıcı alanı koordinatlarında bir THPDFRectangle ve bir TColor; hepsi THPDFViewerModel içinde bir THPDFViewerHighlight kaydı olarak paketlenmiştir. Viewer.HighlightRegion(PageIndex, PageRect, clYellow)'ı, ya da eşdeğeri Model.AddHighlightRegion'ı çağırmak bu kayıtlardan birini özel bir diziye ekler ve dizinini döndürür; ve bu dizin, çağıranın geri aldığı tek tanıtıcıdır: ayrı bir nesne yoktur, referans sayımlı bir arayüz yoktur, serbest bırakılacak hiçbir şey yoktur. Bu makaledeki diğer her yetenek — işareti çizmek, bir yakınlaştırma değişikliğinden sonra yeniden eşlemek, silmek — o tek küçük kaydın üzerine inşa edilmiştir
Her dikdörtgen kabul edilmeden önce normalize edilir ve kırpılır. Bir inceleyici sağdan sola sürüklerse AddHighlightRegion sol ve sağ kenarları değiştirir, yukarı doğru bir sürükleme için üst ve alt kenarları değiştirir, ardından sonucu GetLoadedPageBox üzerinden alınan sayfanın MediaBox'ına karşı kırpar. Sıfır genişlik, sıfır yükseklik veya tamamen sayfa dışında sonuçlanan bir dikdörtgen doğrudan reddedilir: yöntem -1 döndürür ve listeye hiçbir şey eklenmez. Bu dönüş değeri süs değildir: harici bir inceleme dosyasından yeniden oluşturulan veya bir sayfa değiştirildikten sonra eski koordinatlardan gelen bir vurgulama toplu işlemi, çağıran bunu kontrol etmezse sessizce girdi kaybedebilir
Bir vurgulama yakınlaştırma veya döndürmeden sonra nasıl hizalı kalır?
Bir vurgulama hizalı kalır çünkü HotPDF onu PDF sayfa alanında saklar ve yakınlaştırma seviyesi değiştiği anda eskiyecek bir ekran dikdörtgeni saklamak yerine her yeniden boyamada ekran alanına yeniden izdüşürür. THPDFViewerModel.PagePointToView ve tersi ViewPointToPage, bu izdüşümü iki aşamada yapar: önce sayfanın kendi /Rotate girişi, ardından PDF'e asla geri yazılmayan ve yalnızca Görüntüleyicinin gösterdiğini etkileyen Görüntüleyicinin bağımsız ViewRotation'ı. Fare bırakıldığında dönüşümü geri almak aynı iki aşamayı ters sırada çalıştırır; bu da 270 derece döndürülmüş bir sayfada yüksek yakınlaştırmada çizilen bir vurgulamanın, inceleyici görünümü Sayfaya Sığdır'a sıfırladıktan sonra tam olarak doğru yere inmesini sağlayan şeydir
Bu izdüşüm için kullanılan DPI, döndürme kadar önemlidir. HotPDF'in Görüntüleyicisi, her oluşturmadan hemen sonra ekranda şu anda olan bit eşlemenin tam DPI'sini FRenderedDPI'de yakalar ve ImageMouseUp aynı değeri ViewPointToPage'e geçirir; böylece bir fare koordinatı her zaman gerçekten çizildiği çözünürlük kullanılarak dönüştürülür, geçerli yakınlaştırma özelliğinden yeniden hesaplanan bir çözünürlük kullanılarak değil. CreatePageSnapshot ve benzerleri DPI'yi 12 ile 2400 aralığına sınırlar, ancak etkileşimli oluşturma yolu böyle bir tavan taşımaz: standart yakınlaştırma merdiveni %6400'de tepe yapar; bu da varsayılan 96 DPI temel çizgisinde 2400 DPI'nin çok üzerine hesaplanır; bu yüzden koordinat eşleme için bir anlık görüntü tarzı sınırı yeniden kullanmak, yakınlaştırma aralığının tepesinde her vurgulamayı birkaç piksel kaydırırdı. Etkileşimi tamamlayan iki daha küçük varsayılan vardır: her iki eksende de iki pikselden kısa bir sürükleme bir tıklama olarak kabul edilir ve hiçbir vurgulama üretmez; ve vurgulama, FRenderedDPI sıfırdan başladığı için en az bir sayfa gerçekten oluşturulana kadar başlayamaz
Etkileşimli vurgulamayı bir inceleme ekranına bağlamak
Etkileşimli vurgulamayı açmak, THPDFViewer kontrolünün kendisinde üç özelliklik bir iştir: InteractionMode'u varsayılan vimBrowse yerine vimHighlight'a ayarlamak, varsayılan olarak clYellow olan bir HighlightColor seçmek ve inceleyicinin az önce ne çizdiğini öğrenmek için OnMarqueeSelect'i işlemek. Bu olay tetiklenmeden önce diğer her şey — fareyi yakalamak, inceleyici sürüklerken noktalı seçim dikdörtgenini çizmek, bırakma noktasını sayfa alanına geri dönüştürmek, AddHighlightRegion'ı çağırmak — kontrolün içinde gerçekleşir
type
TReviewForm = class(TForm)
Viewer: THPDFViewer;
ReviewLog: TMemo;
procedure FormCreate(Sender: TObject);
private
procedure ViewerMarqueeSelect(Sender: TObject; Shift: TShiftState;
PageIndex: Integer; const PageRect: THPDFRectangle;
HighlightIndex: Integer);
end;
// PdfDoc is a THotPDF already loaded elsewhere on the form
procedure TReviewForm.FormCreate(Sender: TObject);
begin
Viewer.PDFDocument := PdfDoc;
Viewer.InteractionMode := vimHighlight;
Viewer.HighlightColor := clLime;
Viewer.OnMarqueeSelect := ViewerMarqueeSelect;
end;
procedure TReviewForm.ViewerMarqueeSelect(Sender: TObject; Shift: TShiftState;
PageIndex: Integer; const PageRect: THPDFRectangle; HighlightIndex: Integer);
begin
ReviewLog.Lines.Add(Format('page %d, mark #%d at (%.1f, %.1f)-(%.1f, %.1f)',
[PageIndex + 1, HighlightIndex, PageRect.Left, PageRect.Bottom,
PageRect.Right, PageRect.Top]));
end;
OnMarqueeSelect yalnızca gerçekten bir vurgulama üreten bir sürükleme için tetiklenir: bir sürükleme olarak sayılamayacak kadar küçük bir tıklama seçim katmanını hemen temizler ve tamamen sayfa dışına inen bir sürükleme AddHighlightRegion'a ulaşır ancak programatik bir çağrının reddedileceği şekilde orada reddedilir; bu yüzden olay her iki durumda da sessiz kalır. Vurgulama kontrolün kenarlarında yanıt vermeyi durdurmuş gibi görünürse bilmeye değer bir uygulama detayı: fare yakalama, sayfa bit eşlemesini gösteren dahili TImage'a değil, bir TScrollBox türevi olan THPDFViewer'ın kendisine aittir; bu da bir inceleyicinin oluşturulan sayfanın kenarının ötesine sürüklemesine ve yine de temiz bir bırakma almasına izin veren şeydir
Koddan vurgulamalar ekleme, kaldırma ve yeniden okuma
Vurgulamaların hiç fare sürüklemesinden gelmesi gerekmez. Etkileşimli sürüklemenin dahili olarak çağırdığı aynı Model.AddHighlightRegion'a huni olan Viewer.HighlightRegion(PageIndex, PageRect, Color), özellikle bir inceleme ekranının zaten sahip olduğu verilerden vurgulamaları yeniden oluşturabilmesi için genel yapılmıştır: bir veritabanından yüklenen yorumlar, bir metin aramasından sonuçlar veya önceki bir oturumdan geri yüklenen işaretler. Koordinatlar sade PDF kullanıcı alanı sayıları olduğu için, bu yolda hiçbir şey önce bir sayfanın oluşturulmuş olmasına bağlı değildir; FRenderedDPI'nin zaten gerçek bir değer tutmasını gerektiren etkileşimli sürüklemenin aksine
var
I: Integer;
Item: TPriorComment; // your own record: PageIndex + PageRect
NewIndex: Integer;
begin
for I := 0 to PriorComments.Count - 1 do
begin
Item := TPriorComment(PriorComments[I]);
NewIndex := Viewer.HighlightRegion(Item.PageIndex, Item.PageRect, clAqua);
if NewIndex < 0 then
LogWarning('comment %d fell outside the page and was dropped', [I]);
end;
end;
Tek bir vurgulamayı kaldırmak, dizi tabanlı depolamanın kendini gösterdiği yerdir. RemoveHighlightRegion bir kaydı siler ve boşluğu kapatmak için sonraki her kaydı bir konum aşağı kaydırır; bu da daha önce yakalanan herhangi bir dizinin — bir OnMarqueeSelect olayından veya önceki bir numaralandırmadan — listede ondan önce bir şey kaldırıldığında artık güvenilir olmadığı anlamına gelir. OnHighlightChange her ekleme, kaldırma ve ClearHighlightRegions çağrısında tetiklenir, ancak neyin değiştiği hakkında hiçbir bilgi taşımaz; bu yüzden güvenli desen, önbelleğe alınmış bir dizini yerinde yamalamak yerine, bunu bir inceleme panelinin gösterdiği listeyi HighlightCount ve TryGetHighlightRegion'dan yeniden oluşturmak için bir sinyal olarak ele almaktır
procedure TReviewForm.ViewerHighlightChange(Sender: TObject);
var
I: Integer;
Mark: THPDFViewerHighlight;
begin
MarkList.Items.Clear;
for I := 0 to Viewer.Model.HighlightCount - 1 do
if Viewer.Model.TryGetHighlightRegion(I, Mark) then
MarkList.Items.AddObject(Format('page %d', [Mark.PageIndex + 1]),
TObject(I));
end;
Bir işaret ne zaman gerçek bir Highlight açıklamasına dönüşmeli?
Bir vurgulama bölgesi, o tek THPDFViewer örneğinin dışında hayatta kalması gerektiği anda gerçek bir açıklamaya dönüşmelidir. HotPDF ayrıca yeni bir sayfa için AddHighlightAnnotation'ı ve zaten yüklenmiş bir belge için AddLoadedHighlightAnnotation'ı sunar ve neredeyse özdeş isme rağmen, bu tamamen farklı bir mekanizmadır: her ikisi de gerçek bir ISO 32000-1 §12.5.6.10 metin işaretleme açıklamasını, PDF /Subtype /Highlight'ı, sayfanın /Annots dizisine yazar; /QuadPoints tam glif dizisini işaretler ve uyumlu herhangi bir PDF görüntüleyici, dosya kaydedildikten sonra bunu oluşturur, yalnızca HotPDF'in kendisi değil. Aynı mekanizma sınırı, bir işaretin XFDF üzerinden gidip gelip gelmeyeceğine de karar verir: AddLoadedHighlightAnnotation ile oluşturulan bir açıklama, ExportLoadedAnnotationsToXFDF'in aldığı ve Acrobat'a veya başka bir inceleme aracına ISO 19444-1 işaretlemesi olarak verdiği normal bir PDF nesnesidir — Delphi'de PDF açıklamalarını XFDF olarak içe ve dışa aktarma'da ele alınmıştır — AddHighlightRegion aracılığıyla eklenen bir bölge ise bu dışa aktarım için görünmezdir çünkü nesne grafiğine hiç yazılmamıştır: yalnızca onu oluşturan THPDFViewerModel yaşadığı sürece var olur. Bir sayfada bulunan işaretleme ve geometrik açıklama türlerinin tam ailesi ve bir dikdörtgenin her birini nasıl yerleştirdiği, HotPDF ile Delphi'de PDF açıklamaları üzerine makalede ele alınmıştır ve pratik kural basittir: bir belge hâlâ tartışılırken bir işareti tek kullanımlık tutun ve bir karar kesinleştiğinde onu bir açıklamaya sabitleyin
Vurgulama katmanı nerede durur
Vurgulama katmanı, kendi payına, yarı saydam bir vurgulama kalemi gibi görünmeye çalışmaz: RefreshDocument, arama sonuçlarını çizdiği aynı şekilde, altındaki metnin üzerine renkli bir dolgu karıştırmak yerine, önbelleğe alınmış sayfa bit eşlemesinin üzerine her bölgeyi kendi renginde iki piksellik bir çerçeve dikdörtgeni olarak çizer; bu yüzden klasik bir sarı yıkama görünümü uygulama kodunda boyanmalı veya yükseltilmiş bir açıklamanın kendi görünüm akışına ertelenmelidir. Bir bölge var olduğunda yeniden kullanmaya değer bir yetenek, bir vurgulamanın zaten taşıdığı aynı THPDFRectangle'ı alan ve yalnızca o alanı bir bit eşlemeye oluşturan CreateCurrentPageRegionSnapshot'tır; tam sayfayı dışa aktarmadan bir inceleme yorumuna küçük bir önizleme görüntüsü eklemek için kullanışlıdır. Bir inceleme derlemesi iki mekanizma arasında önceden seçim yapmak zorunda değildir: her yeni işareti bir yorum dizisi açık kaldığı sürece tek kullanımlık bir THPDFViewerHighlight bölgesine varsayılan olarak ayarlayın ve bir inceleyici onu çözdüğünde yalnızca AddLoadedHighlightAnnotation'ı çağırın; bu da en çok çalkalanmayı üreten ileri-geri sırasında yüklenen PDF'i dokunulmadan tutar. Burada anlatılan görüntüleyici kontrolü, yukarıda referans verilen açıklama ve form API'lerinin geri kalanının yanı sıra, Delphi ve C++Builder için standart HotPDF Bileşeni'nin bir parçasıdır