Два документи, відкриті одночасно, на тій самій сторінці, кожен у своїй панелі з можливістю прокрутки: це основа програми для порівняння. PDFium Component забезпечує це за допомогою простої об'єктної моделі, де TPdf володіє файлом, а TPdfView володіє відображенням. Один документ, один TPdf, один TPdfView. Хочете три панелі — у вас є три пари. Найскладнішим є не виклики API, а арифметика макета при зміні розміру вікна та логіка синхронізації сторінок, коли ви вирішуєте, який вид повинен слідувати за яким
Макет форми
VCL-форма містить три контейнери TScrollBox пліч-о-пліч, у кожному з яких знаходиться TPdfView, вирівняний по alClient, щоб він заповнював усе вікно. Між контейнерами розташовані два компоненти TSplitter, щоб користувач міг регулювати ширину стовпців під час виконання програми. Панель інструментів над панелями містить кнопки відкриття, елементи керування масштабом та перемикач між двома і трьома видами
Режим трьох видів — це логічне (boolean) значення, яке форма відстежує внутрішньо. Коли воно перемикається, ви перераховуєте ширину і показуєте або приховуєте третій стовпець. Найпростіший підхід — очистити всі властивості Align, приховати сплітери, а потім встановити абсолютні позиції:
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;
Встановлення Align := alNone на всіх трьох контейнерах перед обчисленням цілих чисел дозволяє уникнути конфліктів механізму обмежень VCL із вашими присвоєннями. Відновіть видимість сплітерів після позиціонування, якщо вам потрібна можливість зміни розміру перетягуванням у режимі двох видів
Висота кожного scroll box дорівнює клієнтській області мінус висота панелі інструментів. Оскільки панель інструментів закріплена вгорі за допомогою alTop, ClientHeight - PanelButtons.Height дає вам корисний вертикальний простір. Призначте це всім трьом контейнерам всередині одного виклику UpdateLayout, щоб ніколи не було кадру, де один контейнер вищий за інші, що спричиняє мерехтіння макета
Відкриття документа
Кожна пара панелей потребує власної процедури відкриття. Шаблон короткий: деактивувати компонент, встановити ім'я файлу, активувати, потім перевірити Active; якщо воно залишилося False, запитати пароль і повторити спробу. Зверніть увагу, що TPdfView.Active керує рендерингом, але TPdf.Active — це те, що насправді відкриває файл; вони незалежні. Встановлення PdfView.Active := True, коли зв'язаний з ним TPdf ще не активний, є нешкідливим, але нічого не відображає
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;
Завжди перевіряйте PdfComponent.Active після присвоєння; пошкоджений файл або неправильний пароль призводить до того, що завантаження завершується невдачею без генерації винятку за замовчуванням. Явне встановлення PdfViewComponent.PageNumber := 1 після успішного відкриття дозволяє уникнути застарілого номера сторінки з попереднього документа
Діалогове вікно повідомлення в кінці є навмисним: ви хочете, щоб пошкоджені або непідтримувані файли негайно виявлялися, а не проковтувались як тиха порожня панель. Користувач, який нічого не бачить, не має уявлення, чи завантажився файл і він просто порожній, чи компонент відхилив його. Повідомлення про помилку робить її видимою
Відстеження активної панелі
Коли користувач клацає всередині панелі, ця панель стає активною. Форма відстежує приватне поле FActivePdfView: TPdfView. Візуальний зворотний зв'язок — це зміна кольору межі контейнера TScrollBox: встановіть його на clHighlight для активного та clWindow для інших. Прив'яжіть це до кожного TPdfView.OnClick та до процедури відкриття, щоб фокус слідував за документом, який ви щойно відкрили
Деякі операції застосовуються до всіх видимих панелей, а не лише до активної. Логічне значення FAllViewsMode на формі керує цією гілкою. Коли воно істинне, зміни масштабу та навігація сторінками поширюються на кожну панель, яка має активний документ:
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;
Синхронізована навігація сторінками
Синхронізована навігація є необов'язковою, але корисною для робочих процесів перегляду документів, де обидва файли охоплюють однаковий діапазон сторінок. Логіка належить до обробника подій, який спрацьовує після того, як користувач здійснює навігацію в одному виді. Коли вихідний вид змінює свій PageNumber, обробник передає цей номер іншим видам з урахуванням однієї умови: цільовий вид повинен мати принаймні стільки ж сторінок, інакше дія пропускається
PageNumber у TPdfView та TPdf є незалежними. TPdf.PageNumber відстежує, яку сторінку компонент документа вважає поточною; TPdfView.PageNumber відстежує, що відображається на екрані. Для цілей навігації вам потрібна властивість виду, а не властивість документа
Прапорець з написом на кшталт "Синхронізувати сторінки" надає користувачеві контроль. Коли він знятий, кожна панель здійснює навігацію незалежно, і обробник негайно завершує роботу. Ця незалежність важлива для випадків використання, коли два документи мають різну кількість сторінок, або коли користувач хоче знайти еквівалентний уривок у перекладі, який починається на іншій сторінці. Примусова синхронізація завжди зробила б інструмент складнішим у використанні, ніж просте розміщення двох вікон на робочому столі
На що слід звернути увагу: програмне встановлення PdfView.PageNumber всередині обробника синхронізації само по собі викличе подію зміни для цього виду. Запобігайте нескінченній рекурсії за допомогою логічного прапорця, який ви встановлюєте перед присвоєнням і знімаєте відразу після нього. Прапорець встановлюється для всієї форми, а не для окремого виду, оскільки всі три види спільно використовують один обробник
Масштабування для кожної панелі
Кожен TPdfView має власну властивість Zoom, типу Double у відсотках, де Zoom := 100 означає фактичний розмір (100%). Її встановлення перевизначає будь-який активний FitMode. Для кнопки підгонки за шириною на активній панелі зчитайте масштаб підгонки з PdfView.PageWidthZoom[PdfView.PageNumber] і призначте його. Для підгонки по сторінці використовуйте PageZoom[PageNumber]. Обидві є властивостями-масивами, що індексуються номерами сторінок починаючи з 1, тому перед доступом до них перевіряйте номер сторінки на нуль
Коли ви експортуєте поточну сторінку у зображення, зчитайте обертання з виду, але викликайте RenderPage для компонента TPdf, а не для виду. Формат растрового зображення (bitmap) TPdf.RenderPage приймає точні розміри в пікселях, а також значення TRotation та набір TRenderOptions. Варіант функції повертає TBitmap, яким володіє сторона, що викликає, і який ви повинні звільнити самостійно після збереження:
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;
Множник 2x для ширини та висоти забезпечує чіткіший вихід для документів із дрібним текстом. Блок try/finally навколо звільнення растрового зображення не є необов'язковим; скасування в TSaveDialog все одно потрапляє до блоку finally, і ви хочете, щоб растрове зображення було звільнено незалежно від дій користувача
Вимоги до DLL
PDFium Component є обгорткою над нативною бібліотекою pdfium. 32-розрядний хост-процес потребує pdfium32.dll; 64-розрядний хост потребує pdfium64.dll. Варіанти з рушієм V8 JavaScript додають суфікс v8 і важать приблизно 23-27 МБ порівняно зі стандартними збірками на 5-6 МБ. Для програми перегляду з порівнянням, яка вимикає заповнення форм (Pdf.FormFill := False), стандартної збірки без V8 достатньо, що дозволяє зберегти менший розмір дистрибутива
Розмістіть DLL у тому самому каталозі, що й виконуваний файл, або в будь-якому каталозі в системному PATH. Компонент завантажує її за вимогою, коли активується перший TPdf, тому відсутня DLL виявляється на цьому етапі, а не під час запуску програми. Якщо ви постачаєте інсталятор, найнадійнішим підходом є копіювання DLL у папку програми під час інсталяції, а не покладання на системний каталог, який адміністратор може згодом очистити
Збірки з V8 здебільшого корисні, коли вам потрібно взаємодіяти з діями PDF JavaScript, наприклад, щоб викликати поля обчислень або обробники відправлення. У пасивній програмі перегляду з порівнянням немає причин для запуску JavaScript; встановлення Pdf.FormFill := False перед Active := True повністю пропускає середовище заповнення форм, що також означає, що рушій JS не ініціалізується, навіть якщо використовується стандартна збірка. Це правильне налаштування за замовчуванням для програми перегляду лише для читання незалежно від того, який варіант DLL ви постачаєте
Для отримання детальнішої інформації про PDFium Component та його повний API відвідайте сторінку продукту Delphi PDFium Component