PBToolboxAI v5 ← Site

pdfviewer — u_pbt_pdfviewer #

← Component reference · Guide contents

Built-in PDF viewer: displays a local document or one published on the web, straight inside your window, with pagination, zoom and printing.

▶ See it live — Demo application, PDF viewer tile: the preview, the code behind it and this page, side by side.


At a glance #

Userobjectu_pbt_pdfviewer
Item class— (component without items)
Used forDisplaying an invoice, a purchase order, a contract or a manual without launching an external application
Opt-in options—

The component replaces the classic "save the PDF to a temporary file, then call ShellExecute": the document stays inside your application, and the user never leaves the current screen.


Quick start #

// window open event : display a document stored on disk
uo_pdf.is_source = "C:\factures\FA-2026-0142.pdf"
// ue_load_completed event of uo_pdf : (string as_source)
uo_status.of_panel(/*key*/ "main").is_text = "Document displayed"

That is all: setting is_source is enough to load and display the document.


Properties #

PropertyTypeDefaultPurpose
is_sourcestring""Document to display: a file path (absolute, relative to the application or on the network; a # is part of the name), a file:/// address, a web address https://… served as application/pdf, or a data:application/pdf address. Setting the value triggers the load; setting "" (or blanks only) clears the viewer and keeps its settings. Anything else is refused and reported by ue_load_failed — http:// included, and an address carrying a user name and a password (https://name:secret@…). A PDF held in a blob is shown by of_load_blob. Reads back as you wrote it
ii_pageinteger0Page to open on, counted from 1 (0 = the document's first page). Set while a document is shown, it is that document's page: it loads again, open on it. Set while the viewer is empty, it applies to the next document; otherwise a new document opens at its first page. To open a document on a page, set is_source then ii_page: a single load. Every change reloads the document and raises ue_load_completed again: the reader only reads its page when a document loads; the same value set again does nothing. Write only: reading it back gives the last page you asked for, not the one on screen. The reader is the one built into the web engine, and it reports nothing.
ii_zoominteger0Zoom in percent, from 25 to 500 — the range of the reader's own buttons (0 = left to the viewer). A value outside that range is brought back into it, and reads back as applied. Setting a zoom cancels is_fit, which contradicts it. Write only, like ii_page: if the user zooms with the reader's own toolbar, this property does not follow.
is_fitstring""Fit mode: FIT_PAGE, FIT_WIDTH, FIT_HEIGHT, or "" for none. Cancels ii_zoom
ib_viewer_toolbarbooleantrueShows the viewer's own toolbar (table of contents, page number, zoom, rotate, search, print, save). Hide it when your window carries those commands itself. The reader built into WebView2 has no Full screen command
is_destinationstring""Opens the document on a named destination of the PDF — the target of a bookmark, written by the tool that produced the file. "" for none; a name the document does not have opens it at its first page. Kept for the next documents until you change it. Every change reloads the document on screen
ib_allow_savebooleantrueOffers the Save and Save as commands of the reader's toolbar and of its menu. At false, the document is shown without offering to save a copy. Each viewer keeps its own setting: another window's changes nothing here. It is not a protection: the file stays readable on the disk. Every change reloads the document on screen; set before is_source, the document loads only once
ib_allow_printbooleantrueOffers the Print command of the reader's toolbar and of its menu. of_print still prints: your application decides. Each viewer keeps its own setting. Every change reloads the document on screen; set before is_source, the document loads only once
ib_allow_searchbooleantrueOffers the Search command of the reader's toolbar. The search belongs to the user (Ctrl+F too): the reader offers no search driven by your application. Each viewer keeps its own setting. Every change reloads the document on screen; set before is_source, the document loads once
ib_allow_rotatebooleantrueOffers the Rotate command of the reader's toolbar. Each viewer keeps its own setting. Every change reloads the document on screen; set before is_source, the document loads once
ib_allow_navigation_panebooleantrueOffers the Table of contents button of the reader's toolbar, top left: it opens the side panel with the bookmarks and the page thumbnails. At false, the button goes away. Whether the panel is open is the reader's business and the user's: your application offers the button or not. Each viewer keeps its own setting. Every change reloads the document on screen; set before is_source, the document loads once
is_theme_stylestring""Visual style of the component's own surface: what the viewer shows while it holds no document (before the first one, after is_source = "" or a refused document). The PDF reader keeps its own look: it follows the application theme (PBT_SetDefaultTheme), light or dark only, because WebView2 gives all its PDF readers one colour scheme. Empty = the application's, followed at every change
is_theme_modestring""Light or dark variant of the component's own surface, seen while the viewer holds no document. The PDF reader does not follow it: its toolbar and margins take the light or dark of the application theme (PBT_SetDefaultTheme). Empty = the application's, followed at every change
il_theme_accentlong-1Accent colour of this component (-1 = the application accent, or the theme's). No visible effect here: neither the PDF reader nor the empty viewer draws with an accent. The value is kept and reads back as set

Methods #

MethodPurpose
of_refresh ( )Reads the current document again (from disk or the network) without touching the settings: the way to show a file regenerated at the same path. The scrolling position is not kept: the reader starts again at ii_page. After a failure, tries the same document again. A refused document is judged again: ue_load_failed is raised again. Returns 0 once asked, -4 without a document (is_source empty), -2 if the component is not created
of_load_blob (blob ablb_pdf) · of_load_blob (blob ablb_pdf, string as_name)Shows a PDF held in a blob — read from the database (SELECTBLOB) or received from a service — without writing a file, whatever its size: the bytes stay in memory and are served to this viewer only. The second argument is the name that is_source and ue_load_completed / ue_load_failed give back (document.pdf without it). The blob shown before is released, this one is by the next document or of_reset. Returns 0 once asked (ue_load_completed follows), -5 when the blob is empty or is not a PDF, -4 when it is too large for this process (64 MB in 32 bits, 512 MB in 64 bits) — is_last_error says why —, -2 when the component is not created
of_print ( ) · of_print (boolean)Opens the print preview of the document — not of the page that frames it. Returns 0 once the preview is asked for, -4 when no document is on screen, -2 when the component is not created. The argument has no effect here: it is always the PDF reader's preview
of_print_to_pdf (string) · of_print_to_pdf (string, boolean)Returns -4 on this component, without writing anything: the printed page would only be the reader's frame, never the document. The document already is a PDF: copy the file of is_source
of_reset ( )Clears the viewer and resets every property to its default. Returns 0 once applied, -2 when the component is not created
of_set_redraw (boolean)Groups a burst of changes into a single render. Returns 0 once applied, -2 when the component is not created
of_save_as_png (string) · of_save_as_jpg (string)Exports the rendering as an image. Returns 0 once the image is written, -4 when writing fails, -2 when the component is not created

Events #

EventRaised when
ue_load_completed (string as_source)The document is on screen; as_source is is_source as you wrote it (for a blob, the name given to of_load_blob). Also raised by of_refresh and by every change of page, zoom, fit, viewer toolbar, destination or an ib_allow_* command — set them before is_source for a single load; the same value set again does not raise it. Never raised on a failure
ue_load_failed (string as_source, string as_reason)The document could not be shown; as_reason is one of the REASON_* constants below. The viewer stays empty
ue_link_clicked (string as_url)The user followed a link in the document. The viewer stays on the document: open as_url wherever you want (the system browser, a webbrowser…). as_url is the link's address as is (https://…, mailto:…); a relative link of a local document arrives as a disk path. A link to a local file of the workstation (file:///) in a PDF is not followed: the WebView2 PDF reader blocks it; no ue_link_clicked is raised
ue_ready ( )The component has finished loading; everything sent beforehand has been replayed
ue_runtime_missing ( )The WebView2 runtime is missing: the component stays empty
ue_bg_color (long al_color)The component has computed its theme background color; the userobject has already adopted it (backcolor)

Why a document is not shown #

as_reason of ue_load_failed is one of these u_pbt_pdfviewer constants. A PDF is recognised this way: a local file has the .pdf extension and starts with the %PDF- signature; a remote document is served with the application/pdf type (the only one the engine hands to its reader); a data: address announces application/pdf.

ConstantValueCause
REASON_NOT_FOUND"notfound"Missing or unreadable file, address answering 404
REASON_NOT_PDF"notpdf"Not a PDF: local file without the %PDF- signature, remote answer that is not application/pdf (a file server answering application/octet-stream: download the document first, see the examples), data: or blob of another type
REASON_TOO_LARGE"toolarge"File too large for this process (beyond 64 MB in 32-bit, 512 MB in 64-bit), or a data: address of more than 2 MB of characters (about 1.5 MB of PDF)
REASON_INSECURE"insecure"http://: not supported, serve the document over https://
REASON_UNSUPPORTED"unsupported"Another kind of address (ftp:, blob:…), or an address carrying a user name and a password (https://name:secret@…)
REASON_NETWORK"network"Server unreachable, unknown name, connection cut
REASON_CERTIFICATE"certificate"The site's certificate is invalid, expired or revoked
REASON_AUTH"auth"The site or the proxy asks for authentication: download the document with the restclient (token, credentials), then show it (see the examples)
REASON_HTTP"http"Another server error (403, 500…)
REASON_REFUSED"refused"The site refuses to be shown in a frame, or sends the PDF as a download — the file is not downloaded for all that
REASON_FAILED"failed"Any other cause

What the user can do, without a single line of code #

The viewer displays its built-in toolbar above the document. There is nothing for you to program: it is provided and localized by the system.

ActionHow
PaginationMouse wheel and scroll bar, or type the page number directly in the n / total counter
Zoom+ / − buttons, fit to page or fit to width
SearchThe search button of the toolbar, in the text of the document
PrintingThe printer button of the toolbar (can be hidden with ib_allow_print), or of_print from your code
SavingThe download button, to save a copy of the document (can be hidden with ib_allow_save)
RotationPage rotation from the toolbar menu
KeyboardAs soon as the component has the focus (Tab or of_focus_webview): PageDown, the arrows, Home / End scroll the document, without a click first

The browser's shortcuts — Ctrl+F, Ctrl+P, Ctrl + wheel — are switched off in every component, this one included: use the buttons of the reader's toolbar.

What the reader does not say #

The reader is the one built into the web engine: nothing to install, printing, search and PDF forms included. In return, it reports nothing to your application: not the page on screen, not the number of pages, not the real zoom, not the selected text, and search cannot be driven from code: the reader ignores the open parameters that would highlight a text or open its side panel. It has no Full screen command, and it follows the light or dark theme of the application, never the component's. ii_page and ii_zoom say where to open the document, not where the user is. A password-protected PDF: the reader asks the user for the password itself, your application cannot give it. This is a deliberate choice; a scriptable rendering would need an external library.


Examples #

Opening a local document #

// Absolute path, or relative to the application directory
uo_pdf.is_source = "doc\conditions-generales.pdf"

Opening a document published on the web #

// A web address loads exactly like a local file
uo_pdf.is_source = "https://www.monsite.fr/tarifs/catalogue-2026.pdf"

An internet connection is of course required; the load is asynchronous, and ue_load_completed tells you when it is done.

Displaying the PDF a DataWindow has just produced #

// Local variables
string ls_file

// One file per day, in the temporary folder
ls_file = "C:\temp\report_" + String(Today(), "yyyymmdd") + ".pdf"

// The DataWindow produces the file...
dw_report.SaveAs(ls_file, PDF!, false)

// ...and the viewer displays it immediately
uo_pdf.is_source = ls_file

Refreshing after the file has been regenerated #

// The file was rewritten in the same location : reload without touching is_source
uo_pdf.of_refresh()

Chaining several documents in the same viewer #

// rowfocuschanged event of dw_list : (long currentrow)
// Local variables
string ls_pdf

// The path of the PDF of the current row
ls_pdf = dw_list.GetItemString(currentrow, "pdf_path")

// No attachment empties the viewer (its settings kept) ; otherwise it shows it
if ls_pdf = "" then
	uo_pdf.is_source = ""
else
	uo_pdf.is_source = ls_pdf
end if

Tracking the end of the load #

// ue_load_completed event of uo_pdf : (string as_source)
uo_wait.Hide()

// of_print prints the document on screen
uo_print_button.ib_enabled = true

Saying why the document is not there #

// ue_load_failed event of uo_pdf : (string as_source, string as_reason)
uo_wait.Hide()
choose case as_reason
	case uo_pdf.REASON_NOT_FOUND
		uo_status.of_panel(/*key*/ "main").is_text = "Document not found: " + as_source
	case uo_pdf.REASON_NOT_PDF
		uo_status.of_panel(/*key*/ "main").is_text = "This file is not a PDF"
	case else
		uo_status.of_panel(/*key*/ "main").is_text = "Document unavailable (" + as_reason + ")"
end choose
// ue_link_clicked event of uo_pdf : (string as_url)
// The viewer stays on the document : the link opens in the window's browser
uo_web.is_address = as_url

Printing the document #

// clicked of the Print button : the PDF reader's preview, on the document itself
if uo_pdf.of_print() = -4 then
	uo_status.of_panel(/*key*/ "main").is_text = "No document to print"
end if

Showing without letting save or print #

// A confidential document : neither Save nor Print in the reader's bar
uo_pdf.ib_allow_save = false
uo_pdf.ib_allow_print = false
uo_pdf.is_source = is_current_document

Set them before is_source: the document loads only once. Each viewer keeps its own setting. It is not a protection: the file stays readable on the disk, and of_print still prints.

Preview in a tab, next to data entry #

// open event : the viewer takes one tab page, data entry the other
uo_tab.of_add_page(/*key*/ "entry",  /*title*/ "Entry",   /*page*/ uo_page_entry)
uo_tab.of_add_page(/*key*/ "preview",  /*title*/ "Preview",   /*page*/ uo_page_preview)

// The viewer sits inside uo_page_preview like any other control
uo_pdf.is_source = is_current_document

The component is hosted with no special precautions inside a tab or a dockcontainer panel.

Checking the file before displaying it #

// Local variables
string ls_path

// The file of the invoice on screen
ls_path = "C:\factures\" + is_number + ".pdf"

// No file, nothing to show : clear the viewer rather than leave the previous document
if not FileExists(ls_path) then
    uo_pdf.is_source = ""
    uo_status.of_panel(/*key*/ "main").is_text = "Invoice not found"
    return
end if

// Otherwise, the invoice is shown
uo_pdf.is_source = ls_path

Showing a PDF read from the database (blob) #

// Local variables
blob lblb_pdf

// The invoice stored in the database, read into a blob
SELECTBLOB pdf_file INTO :lblb_pdf FROM invoice WHERE invoice_no = :is_number USING SQLCA;

// Shown straight from memory : no temporary file, whatever its size
if SQLCA.SQLCode = 0 then
	uo_pdf.of_load_blob(/*pdf*/ lblb_pdf, /*name*/ "invoice-" + is_number + ".pdf")
end if

The name given comes back in is_source and in ue_load_completed; the blob is released by the next document or by of_reset.

Opening on a chapter #

// Opened on the chapter "prices" of the catalogue; the reader's Table of contents button lists the bookmarks
uo_pdf.is_destination = "prices"
uo_pdf.is_source = "doc\catalogue-2026.pdf"

The name of a destination is the one written by the tool that produced the PDF: your document generator knows it.

A document behind a token, or served as octet-stream #

// Local variables
long ll_status
string ls_file

// The restclient (inv_rest, set up when the window opened) sends the token and downloads the file
ls_file = "C:\temp\invoice-4152.pdf"
ll_status = inv_rest.of_download(/*url*/ "/orders/4152/invoice.pdf", /*path*/ ls_file)

// Then the viewer shows the file, whatever type the server announced
if ll_status = 200 then
	uo_pdf.is_source = ls_file
end if

The engine's reader can neither send a token nor show an application/octet-stream answer (REASON_AUTH, REASON_NOT_PDF): the restclient does it for it.


Accepted formats and paths #

Form of is_sourceExampleNote
Absolute path"C:\docs\contrat.pdf"The most reliable
Relative path"doc\notice.pdf"Relative to the application directory
Network path"\\serveur\partage\bon.pdf"The user must have read permissions
Name with #"C:\devis\Devis #12.pdf"The # is part of the file name
file: address"file:///C:/docs/contrat.pdf"Converted to a path, like an absolute path; file://localhost/C:/… and the four-slash UNC form file:////server/share/… too
Web address"https://…/catalogue.pdf"Served as application/pdf, asynchronous load. A #page=… written in the address is ignored: use ii_page. An address carrying credentials (https://name:secret@…) is refused (REASON_UNSUPPORTED): go through the restclient
In-memory document"data:application/pdf;base64,…"Nothing is written to disk. Beyond 2 MB of characters (about 1.5 MB of PDF), ue_load_failed with REASON_TOO_LARGE: for a larger document held in memory, of_load_blob
http:// address"http://intranet/bon.pdf"Not supported: ue_load_failed with REASON_INSECURE. Serve the document over https://
Empty""Clears the viewer and keeps its settings; a value made of blanks too

This component only supports PDF: anything else is refused and reported by ue_load_failed (REASON_NOT_PDF). For an image, use picture; for an HTML page, webbrowser.


Best practices #

Inherited from the common base #

These members exist on every visual component — they are not specific to this one. They are detailed once, in the transverse chapters; this table only says where to read them.

MembersRoleDetailed in
of_resetPut the component back to zero3.6 Resetting a component: of_reset()
of_register_shortcut · of_clear_shortcutsThe component's keyboard chords3.5 Keyboard shortcuts
ib_created · ib_ready · is_last_errorWhether it was born, whether it is ready, what failed3.7 Diagnostics
of_save_as_png · of_save_as_jpgExport the rendering as an image3.8 Exporting the rendering as an image
of_set_redrawGroup changes into a single repaint3.10 Best practices
of_preload_iconsIcons shown with no delayInstant display: of_icon
of_set_translationTranslate one of the component's labels5.2 Adapting a label: of_set_translation
of_focus_webviewGive the component the focus6.4 Keyboard and focus
of_print · of_print_to_pdfPrint, or write a PDF6.9 Printing
of_set_property · of_get_property · is_component_nameDriving a property by its name3.1 The property engine
is_accessible_name · is_accessible_descriptionThe name and description a screen reader says3.4 Optional properties and events

Two helpers are not inherited: of_icon and of_escape_markup live on n_pbt_utils. Declare one — n_pbt_utils lnv_utils, nothing to create — and call them on it.


← Component reference · Guide contents