Dwa dokumenty otwarte jednocześnie, ten sam numer strony, każdy we własnym przewijalnym panelu: to jest sedno przeglądarki porównawczej. PDFium Component realizuje to poprzez prosty model obiektowy, w którym TPdf jest właścicielem pliku, a TPdfView jest właścicielem wyświetlania. Jeden dokument, jeden TPdf, jeden TPdfView. Chcesz trzech paneli, masz trzy pary. Trudne części to nie wywołania API; to arytmetyka układu, gdy okno zmienia rozmiar, oraz logika synchronizacji stron, gdy decydujesz, który widok ma podążać za którym
Układ formularza
Formularz VCL przechowuje trzy kontenery TScrollBox obok siebie, każdy z TPdfView w środku i wyrównany do alClient, aby wypełnić pole. Dwa komponenty TSplitter siedzą między polami, tak aby użytkownik mógł dostosować szerokości kolumn w czasie działania. Pasek narzędzi nad panelami niesie przyciski otwierania, elementy sterujące powiększeniem oraz przełącznik dwa widoki / trzy widoki
Tryb trzech widoków to wartość logiczna, którą formularz śledzi wewnętrznie. Gdy się przełącza, przeliczasz szerokości i pokazujesz lub ukrywasz trzecią kolumnę. Najprostszym podejściem jest wyczyszczenie wszystkich właściwości Align, ukrycie splitterów, a następnie ustawienie pozycji bezwzględnych:
procedure TFormMain.UpdateLayout;
var
TotalWidth: Integer;
begin
TotalWidth := ClientWidth;
if ThreeViewMode then
begin
ScrollBox3.Visible := True;
ScrollBox1.Left := 0;
ScrollBox1.Width := TotalWidth div 3;
ScrollBox2.Left := ScrollBox1.Width;
ScrollBox2.Width := TotalWidth div 3;
ScrollBox3.Left := ScrollBox2.Left + ScrollBox2.Width;
ScrollBox3.Width := TotalWidth - ScrollBox3.Left;
// Apply the same (ClientHeight - toolbar height) to all three Height values
end
else
begin
ScrollBox3.Visible := False;
ScrollBox1.Left := 0;
ScrollBox1.Width := TotalWidth div 2;
ScrollBox2.Left := ScrollBox1.Width;
ScrollBox2.Width := TotalWidth - ScrollBox2.Left;
end;
end;
Ustawienie Align := alNone na wszystkich trzech polach przed arytmetyką na liczbach całkowitych zapobiega temu, by silnik ograniczeń VCL walczył z twoimi przypisaniami. Przywróć widoczność splitterów po pozycjonowaniu, jeśli chcesz zmiany rozmiaru przez przeciąganie w trybie dwóch widoków
Wysokość każdego pola przewijania to obszar klienta minus wysokość panelu paska narzędzi. Ponieważ pasek narzędzi jest zadokowany u góry z alTop, ClientHeight - PanelButtons.Height daje użyteczną przestrzeń pionową. Przypisz to do wszystkich trzech pól wewnątrz tego samego wywołania UpdateLayout, tak aby nigdy nie było klatki, w której jedno pole jest wyższe od pozostałych i powoduje migotanie układu
Otwieranie dokumentu
Każda para paneli potrzebuje własnej procedury otwierania. Wzorzec jest krótki: dezaktywuj komponent, ustaw nazwę pliku, aktywuj, następnie sprawdź Active; jeśli pozostała na False, poproś o hasło i spróbuj ponownie. Zauważ, że TPdfView.Active kontroluje renderowanie, ale to TPdf.Active faktycznie otwiera plik; są one niezależne. Ustawienie PdfView.Active := True, gdy powiązany TPdf nie jest jeszcze aktywny, jest nieszkodliwe, ale niczego nie wyświetla
procedure TFormMain.OpenPdfFile(PdfComponent: TPdf;
PdfViewComponent: TPdfView);
var
Password: string;
begin
if not OpenDialog.Execute then
Exit;
PdfComponent.Active := False;
PdfComponent.FileName := OpenDialog.FileName;
PdfComponent.Password := '';
PdfComponent.Active := True;
// Load failures are silent: Active stays False instead of raising.
if not PdfComponent.Active then
begin
// Most likely a password-protected file; give the user one retry.
if InputQuery('Password', 'Enter document password:', Password) then
begin
PdfComponent.Password := Password;
PdfComponent.Active := True;
end;
end;
if not PdfComponent.Active then
begin
ShowMessage('Could not open ' + OpenDialog.FileName +
' (damaged file or wrong password)');
Exit;
end;
PdfViewComponent.PageNumber := 1;
SetActivePdfView(PdfViewComponent);
end;
Zawsze sprawdzaj PdfComponent.Active po przypisaniu; uszkodzony plik lub złe hasło powoduje ciche niepowodzenie ładowania bez zgłaszania wyjątku na domyślnej ścieżce. Jawne ustawienie PdfViewComponent.PageNumber := 1 po pomyślnym otwarciu zapobiega przeniesieniu nieaktualnego numeru strony z poprzedniego dokumentu
Okno dialogowe z komunikatem na końcu jest zamierzone: chcesz, aby uszkodzone lub nieobsługiwane pliki ujawniały się natychmiast, a nie były połykane jako cichy pusty panel. Użytkownik, który nic nie widzi, nie ma pojęcia, czy plik się załadował i jest po prostu pusty, czy też komponent go odrzucił. Raportowanie niepowodzenia utrzymuje błąd widocznym
Śledzenie aktywnego panelu
Gdy użytkownik kliknie wewnątrz panelu, ten panel staje się aktywny. Formularz śledzi prywatne pole FActivePdfView: TPdfView. Wizualną informacją zwrotną jest zmiana koloru obramowania na zawierającym TScrollBox: ustaw go na clHighlight dla aktywnego i clWindow dla pozostałych. Podepnij to do każdego TPdfView.OnClick oraz do procedury otwierania, tak aby fokus podążał za dokumentem, który właśnie otworzyłeś
Niektóre operacje dotyczą wszystkich widocznych paneli, a nie tylko aktywnego. Wartość logiczna FAllViewsMode na formularzu steruje tą gałęzią. Gdy jest prawdziwa, zmiany powiększenia i nawigacja po stronach rozchodzą się na każdy panel, który ma aktywny dokument:
procedure TFormMain.ApplyZoomToAll(NewZoom: Double);
begin
if PdfView1.Active then PdfView1.Zoom := NewZoom;
if PdfView2.Active then PdfView2.Zoom := NewZoom;
if ThreeViewMode and PdfView3.Active then PdfView3.Zoom := NewZoom;
end;
Zsynchronizowana nawigacja po stronach
Zsynchronizowana nawigacja jest opcjonalna, ale przydatna w przepływach pracy dotyczących rewizji dokumentów, gdzie oba pliki obejmują ten sam zakres stron. Logika należy do procedury obsługi zdarzenia, która uruchamia się po tym, jak użytkownik nawiguje jednym widokiem. Gdy widok źródłowy zmienia swój PageNumber, procedura propaguje ten numer do pozostałych widoków, z zastrzeżeniem jednego zabezpieczenia: widok docelowy musi mieć co najmniej tyle stron, w przeciwnym razie pomiń
PageNumber na TPdfView i na TPdf są niezależne. TPdf.PageNumber śledzi, którą stronę komponent dokumentu uznaje za bieżącą; TPdfView.PageNumber śledzi to, co jest wyświetlane na ekranie. Do celów nawigacji chcesz właściwości widoku, a nie właściwości dokumentu
Pole wyboru z etykietą w rodzaju „Synchronizuj strony” daje użytkownikowi kontrolę. Gdy jest odznaczone, każdy panel nawiguje niezależnie, a procedura obsługi natychmiast kończy działanie. Ta niezależność jest ważna w przypadkach użycia, w których oba dokumenty mają różną liczbę stron albo w których użytkownik chce znaleźć równoważny fragment w tłumaczeniu, które zaczyna się na innej stronie. Wymuszanie synchronizacji na stałe uczyniłoby narzędzie trudniejszym w użyciu niż zwykłe ustawienie dwóch okien pulpitu
Jedna rzecz do pilnowania: programowe ustawienie PdfView.PageNumber wewnątrz procedury obsługi synchronizacji samo wyzwoli zdarzenie zmiany na tym widoku. Zabezpiecz się przed nieskończoną rekurencją flagą logiczną, którą ustawiasz przed przypisaniem i czyścisz natychmiast po nim. Flaga jest per-formularz, a nie per-widok, ponieważ wszystkie trzy widoki dzielą tę samą procedurę obsługi
Powiększenie per panel
Każdy TPdfView niesie własną właściwość Zoom, Double w procentach, gdzie Zoom := 100 oznacza rzeczywisty rozmiar (100%). Ustawienie jej nadpisuje dowolny aktywny FitMode. Dla przycisku dopasowania do szerokości na aktywnym panelu odczytaj powiększenie dopasowania z PdfView.PageWidthZoom[PdfView.PageNumber] i przypisz je. Dla dopasowania do strony użyj PageZoom[PageNumber]. Obie są właściwościami tablicowymi indeksowanymi numerem strony liczonym od 1, więc zabezpiecz się przed zerowym numerem strony przed dostępem do nich
Gdy eksportujesz bieżącą stronę do obrazu, odczytaj obrót z widoku, ale wywołaj RenderPage na komponencie TPdf, a nie na widoku. Bitmapowa forma TPdf.RenderPage przyjmuje jawne wymiary w pikselach plus wartość TRotation i zbiór TRenderOptions. Wariant funkcyjny zwraca TBitmap będący własnością wywołującego, który zwalniasz samodzielnie po zapisaniu:
procedure TFormMain.SaveActiveViewAsImage;
var
Pdf: TPdf;
Bmp: TBitmap;
Jpeg: TJpegImage;
begin
if not Assigned(FActivePdfView) or not FActivePdfView.Active then
Exit;
Pdf := FActivePdfView.Pdf;
Pdf.PageNumber := FActivePdfView.PageNumber;
Bmp := Pdf.RenderPage(
0, 0,
Round(Pdf.PageWidth * 2),
Round(Pdf.PageHeight * 2),
FActivePdfView.Rotation, [], clWhite);
try
if SavePictureDialog.Execute then
begin
Jpeg := TJpegImage.Create;
try
Jpeg.Assign(Bmp);
Jpeg.CompressionQuality := 90;
Jpeg.SaveToFile(SavePictureDialog.FileName);
finally
Jpeg.Free;
end;
end;
finally
Bmp.Free;
end;
end;
Mnożnik 2x na szerokości i wysokości daje ostrzejszy wynik dla dokumentów z drobnym tekstem. try/finally wokół zwolnienia bitmapy nie jest opcjonalne; anulowanie TSaveDialog i tak trafia w blok finally, a ty chcesz, aby bitmapa została zwolniona niezależnie od tego, co zrobił użytkownik
Wymagania dotyczące bibliotek DLL
PDFium Component owija natywną bibliotekę pdfium. 32-bitowy proces hosta potrzebuje pdfium32.dll; 64-bitowy host potrzebuje pdfium64.dll. Warianty z silnikiem JavaScript V8 dodają przyrostek v8 i ważą mniej więcej 23-27 MB w porównaniu do standardowych buildów o rozmiarze 5-6 MB. Dla przeglądarki porównawczej, która wyłącza wypełnianie formularzy (Pdf.FormFill := False), standardowy build bez V8 jest wystarczający i utrzymuje mniejszy rozmiar dystrybucji
Umieść bibliotekę DLL w tym samym katalogu co plik wykonywalny albo w dowolnym katalogu na systemowej ścieżce PATH. Komponent ładuje ją na żądanie, gdy pierwszy TPdf jest aktywowany, więc brakująca DLL ujawnia się w tym momencie, a nie przy starcie aplikacji. Jeśli dostarczasz instalator, najbardziej niezawodnym podejściem jest skopiowanie biblioteki DLL do folderu aplikacji podczas instalacji, zamiast polegania na katalogu systemowym, który administrator może później wyczyścić
Buildy V8 są przede wszystkim przydatne, gdy musisz wchodzić w interakcję z akcjami JavaScript w PDF, na przykład aby wyzwolić pola obliczeniowe lub procedury obsługi wysyłania. Pasywna przeglądarka porównawcza nie ma powodu, by uruchamiać JavaScript; ustawienie Pdf.FormFill := False przed Active := True całkowicie pomija środowisko wypełniania formularzy, co oznacza również, że żaden silnik JS nie jest inicjalizowany, nawet jeśli używany jest standardowy build. To jest poprawne ustawienie domyślne dla przeglądarki tylko do odczytu, niezależnie od tego, który wariant DLL dostarczasz
Więcej szczegółów o PDFium Component i jego pełnym API znajdziesz na stronie produktu Delphi PDFium Component