Technical Article

Creating a Multi-Page PDF Viewer with Continuous Scrolling in Delphi

· PDF Programming

This tutorial explores the Multi-Page Viewer demo, which extends the basic PDF viewer with continuous scrolling capabilities. This viewing mode is similar to how modern PDF readers like Adobe Acrobat display documents, allowing users to scroll through all pages seamlessly.

Overview

The Multi-Page Viewer demo showcases advanced viewing modes in PDFium VCL, including single-page continuous scrolling and dual-page continuous scrolling (book mode). These features are essential for creating a professional PDF reading experience.

Display Modes

PDFium VCL supports multiple display modes through the DisplayMode property:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type
  TPdfDisplayMode = (
    dmSingleContinuous,  // Single page with continuous vertical scrolling
    dmDoubleContinuous   // Two pages side-by-side (book layout)
  );
 
// Switch between display modes
procedure TFormMain.ComboBoxDisplayModeChange(Sender: TObject);
begin
  case ComboBoxDisplayMode.ItemIndex of
    0: PdfView.DisplayMode := dmSingleContinuous;
    1: PdfView.DisplayMode := dmDoubleContinuous;
  end;
end;

Key Features

  • Continuous Scrolling – Scroll through all pages without page-by-page navigation
  • Dual-Page Mode – View two pages side-by-side like a physical book
  • Text Selection Across Pages – Select and copy text spanning multiple pages
  • Bookmark Navigation – Jump to bookmarked sections instantly
  • Search with Highlighting – Find and highlight text across the document
  • Keyboard Navigation – Arrow keys, Page Up/Down, Home/End support
  • Optimised Performance – Renders only visible pages for smooth scrolling

PDFium DLL Requirements

Before running any PDFium VCL application, you must install the PDFium DLL files. The DLLs are located in the DLLs folder:

  • pdfium32.dll / pdfium64.dll – Standard versions for most applications
  • pdfium32v8.dll / pdfium64v8.dll – Extended versions with V8 JavaScript engine

Installation: Run PDFiumVCL\DLLs\CopyDlls.bat as Administrator to copy DLLs to Windows system directories. On 64-bit Windows, 32-bit DLLs go to SysWOW64 and 64-bit DLLs go to System32.

Setting Up the Component

The demo implements a sophisticated text selection system:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
procedure TFormMain.PdfViewMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  CharIndex: Integer;
begin
  if Button = mbLeft then
  begin
    // Get character index at click position
    CharIndex := GetPreciseCharacterIndex(X, Y);
    
    if CharIndex >= 0 then
    begin
      SelectionMode := True;
      Selecting := True;
      SelectionStart := CharIndex;
      SelectionEnd := CharIndex;
    end;
  end;
end;
 
procedure TFormMain.PdfViewMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
var
  CharIndex: Integer;
begin
  if Selecting then
  begin
    CharIndex := GetPreciseCharacterIndex(X, Y);
    if CharIndex >= 0 then
    begin
      SelectionEnd := CharIndex;
      PdfView.Invalidate; // Redraw to show selection
    end;
  end;
end;

Character Index Detection

For precise text selection, the demo uses zoom-aware tolerance for character detection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function TFormMain.GetPreciseCharacterIndex(X, Y: Integer): Integer;
var
  BaseTolerance: Single;
  ZoomFactor: Single;
  AdjustedTolerance: Single;
  CharIndex: Integer;
begin
  Result := -1;
  
  if not PdfView.Active then
    Exit;
    
  // Calculate dynamic tolerance based on zoom level
  ZoomFactor := PdfView.Zoom;
  BaseTolerance := 5.0;
  
  // Adjust tolerance inversely to zoom
  AdjustedTolerance := BaseTolerance / ZoomFactor;
  
  // Get character at position with tolerance
  CharIndex := PdfView.CharacterIndexAtPos(X, Y,
    AdjustedTolerance, AdjustedTolerance);
    
  Result := CharIndex;
end;

Highlighting Selected Text

The demo draws selection highlights in the OnPaint event:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
procedure TFormMain.PdfViewPaint(Sender: TObject);
var
  I, StartIdx, EndIdx: Integer;
  Rect: TPdfRectangle;
  R: TRect;
begin
  if (SelectionStart >= 0) and (SelectionEnd >= 0) then
  begin
    StartIdx := Min(SelectionStart, SelectionEnd);
    EndIdx := Max(SelectionStart, SelectionEnd);
    
    // Draw highlight for each character in selection
    for I := StartIdx to EndIdx do
    begin
      Rect := PdfView.CharacterRectangle[I];
      
      // Convert PDF coordinates to screen coordinates
      if PdfView.PageToDevice(
        Rect.Left, Rect.Top,
        PdfView.Left, PdfView.Top,
        PdfView.Width, PdfView.Height,
        PdfView.Rotation, R.Left, R.Top) then
      begin
        // Draw highlight rectangle
        PdfView.Canvas.Brush.Colour := clHighlight;
        PdfView.Canvas.Brush.Style := bsSolid;
        PdfView.Canvas.FillRect(R);
      end;
    end;
  end;
end;

Copy Selected Text to Clipboard

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
procedure TFormMain.MenuItemCopyClick(Sender: TObject);
var
  StartIdx, EndIdx: Integer;
  SelectedText: string;
begin
  if (SelectionStart >= 0) and (SelectionEnd >= 0) then
  begin
    StartIdx := Min(SelectionStart, SelectionEnd);
    EndIdx := Max(SelectionStart, SelectionEnd);
    
    // Set page number for text extraction
    Pdf.PageNumber := PdfView.PageNumber;
    
    // Extract text from selection range
    SelectedText := Pdf.Text(StartIdx, EndIdx - StartIdx + 1);
    
    // Copy to clipboard
    Clipboard.AsText := SelectedText;
  end;
end;

Search Functionality

Implement text search with highlighting:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
procedure TFormMain.SpeedButtonSearchClick(Sender: TObject);
var
  SearchText: string;
  FoundIndex: Integer;
begin
  SearchText := EditSearch.Text;
  
  if SearchText = '' then
    Exit;
    
  // Find first occurrence
  FoundIndex := Pdf.FindFirst(SearchText, [], 0, True);
  
  if FoundIndex >= 0 then
  begin
    SearchStart := FoundIndex;
    SearchEnd := FoundIndex + Length(SearchText) - 1;
    PdfView.Invalidate; // Redraw to show highlight
  end
  else
    ShowMessage('Text not found');
end;

Page Change Event

Handle page changes to update UI elements:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
procedure TFormMain.PdfViewPageChanged(Sender: TObject);
begin
  if PdfView.Active then
  begin
    // Update page indicator
    SpeedButtonPageNumber.Caption :=
      IntToStr(PdfView.PageNumber) + ' of ' + IntToStr(PdfView.PageCount);
    
    // Update navigation button states
    SpeedButtonFirstPage.Enabled := PdfView.PageNumber > 1;
    SpeedButtonPreviousPage.Enabled := PdfView.PageNumber > 1;
    SpeedButtonNextPage.Enabled := PdfView.PageNumber < PdfView.PageCount;
    SpeedButtonLastPage.Enabled := PdfView.PageNumber < PdfView.PageCount;
    
    // Update bookmark tree selection
    if not DisableBookmarks then
      UpdateBookmarkSelection(PdfView.PageNumber);
  end;
end;

Keyboard Navigation

The demo handles keyboard shortcuts for navigation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
procedure TFormMain.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  case Key of
    VK_ESCAPE:
      begin
        // Exit selection mode on Escape
        if SelectionMode then
        begin
          SelectionMode := False;
          SelectionStart := -1;
          SelectionEnd := -1;
          PdfView.Invalidate;
        end;
        Key := 0;
      end;
      
    VK_PRIOR, VK_UP, VK_LEFT:
      begin
        if SpeedButtonPreviousPage.Enabled then
          SpeedButtonPreviousPage.Click;
        Key := 0;
      end;
      
    VK_NEXT, VK_DOWN, VK_RIGHT:
      begin
        if SpeedButtonNextPage.Enabled then
          SpeedButtonNextPage.Click;
        Key := 0;
      end;
      
    VK_HOME:
      begin
        if SpeedButtonFirstPage.Enabled then
          SpeedButtonFirstPage.Click;
        Key := 0;
      end;
      
    VK_END:
      begin
        if SpeedButtonLastPage.Enabled then
          SpeedButtonLastPage.Click;
        Key := 0;
      end;
  end;
end;

Dual-Page Navigation

When in dual-page mode, navigate by two pages at a time:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
procedure TFormMain.SpeedButtonNextPageClick(Sender: TObject);
begin
  if PdfView.DisplayMode = dmSingleContinuous then
    PdfView.PageNumber := PdfView.PageNumber + 1
  else
    PdfView.PageNumber := PdfView.PageNumber + 2;
end;
 
procedure TFormMain.SpeedButtonPreviousPageClick(Sender: TObject);
begin
  if PdfView.DisplayMode = dmSingleContinuous then
    PdfView.PageNumber := PdfView.PageNumber - 1
  else
    PdfView.PageNumber := PdfView.PageNumber - 2;
end;

Conclusion

The Multi-Page Viewer demo shows how to build a feature-complete PDF reader with continuous scrolling, text selection, and search functionality. These are the features users expect from modern PDF viewing applications.

PDFium VCL handles the complex rendering and text extraction, while you focus on the user interface and application-specific features. The result is a smooth, responsive PDF viewing experience that rivals commercial PDF readers.

Get PDFium VCL Component at loslab.com and start building professional PDF applications in Delphi today.