Eightbot.MauiNativePdfView 1.0.7

Prefix Reserved
dotnet add package Eightbot.MauiNativePdfView --version 1.0.7
                    
NuGet\Install-Package Eightbot.MauiNativePdfView -Version 1.0.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Eightbot.MauiNativePdfView" Version="1.0.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Eightbot.MauiNativePdfView" Version="1.0.7" />
                    
Directory.Packages.props
<PackageReference Include="Eightbot.MauiNativePdfView" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Eightbot.MauiNativePdfView --version 1.0.7
                    
#r "nuget: Eightbot.MauiNativePdfView, 1.0.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Eightbot.MauiNativePdfView@1.0.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Eightbot.MauiNativePdfView&version=1.0.7
                    
Install as a Cake Addin
#tool nuget:?package=Eightbot.MauiNativePdfView&version=1.0.7
                    
Install as a Cake Tool

<div align="center">

MauiNativePdfView

<img src="images/icon.svg" width="200" />

A high-performance, cross-platform PDF viewer for .NET MAUI

NuGet NuGet Downloads

License: MIT .NET MAUI

Native PDF renderingZero web dependenciesFull feature parity

FeaturesInstallationQuick StartDocumentationExamples

</div>


🎯 Overview

MauiNativePdfView brings native PDF viewing capabilities to your .NET MAUI applications by wrapping platform-native controls. Unlike WebView-based solutions, this library provides true native performance with full access to platform-specific PDF features.

Why MauiNativePdfView?

  • 🚀 Native Performance - Uses PDFKit (iOS) and AhmerPdfium (Android) for optimal speed
  • 💪 Feature Complete - Comprehensive API with zoom, navigation, annotations, and events
  • 🎨 Consistent API - Write once, works on both iOS and Android
  • 📦 Zero Web Dependencies - No WebView, no JavaScript, pure native rendering
  • 🔧 Highly Configurable - Extensive properties for customization
  • 📱 Production Ready - Battle-tested with full annotation support

✨ Features

Core Functionality

  • Multiple PDF Sources - Load from files, URLs, streams, byte arrays, or embedded assets
  • Password Protection - Full support for encrypted PDFs
  • Zoom & Gestures - Pinch-to-zoom, double-tap zoom, with configurable min/max levels
  • Page Navigation - Swipe between pages, programmatic navigation, page events
  • Link Interception - Intercept and handle link taps before navigation (both platforms)
  • Link Handling - Automatic detection and handling of internal/external links
  • Display Modes - Single page or continuous scrolling
  • Scroll Orientation - Vertical or horizontal page layout

Advanced Features

  • Annotation Rendering - Toggle PDF annotations on/off
  • Annotation Events - Tap detection with annotation details (iOS)
  • Tap Gesture Control - Enable/disable custom tap interception
  • Quality Control - Antialiasing and rendering quality settings
  • Background Color - Customizable viewer background
  • Page Spacing - Adjustable spacing between pages
  • Event System - Comprehensive events for document lifecycle

Events

  • DocumentLoaded - Fires when PDF is loaded with page count and metadata
  • PageChanged - Current page and total page count updates
  • LinkTapped - Intercept link taps before navigation (set e.Handled = true to prevent)
  • Tapped - General tap events with page coordinates (requires EnableTapGestures = true)
  • AnnotationTapped - Annotation tap with type, content, and bounds (iOS)
  • Rendered - Initial rendering complete
  • Error - Error handling with detailed messages

📦 Installation

NuGet Package Manager

dotnet add package MauiNativePdfView

Package Manager Console

Install-Package MauiNativePdfView

Requirements

  • .NET 9.0 or later
  • iOS 12.2+ (PDFKit)
  • Android 7.0+ (API 24+)

🚀 Quick Start

1. Add Namespace

Option A: Custom Schema (Recommended)

xmlns:pdf="http://eightbot.com/maui/pdfview"

Option B: CLR Namespace

xmlns:pdf="clr-namespace:MauiNativePdfView;assembly=MauiNativePdfView"

2. Basic XAML


<pdf:PdfView Source="https://example.com/document.pdf" />


<pdf:PdfView x:Name="pdfViewer"
             EnableZoom="True"
             EnableSwipe="True"
             DocumentLoaded="OnDocumentLoaded"
             PageChanged="OnPageChanged" />

String to PdfSource Conversion

The library supports automatic string conversion in XAML:


<pdf:PdfView Source="https://example.com/document.pdf" />


<pdf:PdfView Source="sample.pdf" />


<pdf:PdfView Source="asset://documents/guide.pdf" />


<pdf:PdfView Source="file:///path/to/document.pdf" />

3. Load a PDF (Code-Behind)

// From file
pdfViewer.Source = PdfSource.FromFile("/path/to/document.pdf");

// From URL
pdfViewer.Source = PdfSource.FromUri(new Uri("https://example.com/doc.pdf"));

// From embedded asset
pdfViewer.Source = PdfSource.FromAsset("sample.pdf");

// From stream
pdfViewer.Source = PdfSource.FromStream(myStream);

// From byte array
pdfViewer.Source = PdfSource.FromBytes(pdfBytes);

// With password
pdfViewer.Source = PdfSource.FromFile("/path/to/encrypted.pdf", "password");

4. Handle Events

private void OnDocumentLoaded(object sender, DocumentLoadedEventArgs e)
{
    Console.WriteLine($"Loaded: {e.PageCount} pages");
    Console.WriteLine($"Title: {e.Title}");
    Console.WriteLine($"Author: {e.Author}");
}

private void OnPageChanged(object sender, PageChangedEventArgs e)
{
    Console.WriteLine($"Page {e.PageIndex + 1} of {e.PageCount}");
}

📖 Documentation

PdfView Properties

Property Type Default Description
Source PdfSource null PDF source to display
EnableZoom bool true Enable pinch-to-zoom
EnableSwipe bool true Enable swipe gestures
EnableTapGestures bool false Enable tap interception
EnableLinkNavigation bool true Enable clickable links
Zoom float 1.0f Current zoom level
MinZoom float 1.0f Minimum zoom level
MaxZoom float 3.0f Maximum zoom level
PageSpacing int 10 Spacing between pages (pixels)
FitPolicy FitPolicy Width How pages fit on screen
DisplayMode PdfDisplayMode SinglePageContinuous Page display mode
ScrollOrientation PdfScrollOrientation Vertical Scroll direction
DefaultPage int 0 Initial page (0-based)
EnableAntialiasing bool true Antialiasing (Android only)
UseBestQuality bool true Best quality rendering
BackgroundColor Color null Viewer background color
EnableAnnotationRendering bool true Show PDF annotations
CurrentPage int 0 Current page (readonly)
PageCount int 0 Total pages (readonly)

PdfSource Types

The PdfSource class supports automatic string conversion via implicit operators and TypeConverter, making it easy to use in both XAML and code.

Factory Methods (Code-Behind):

// File path
var source = PdfSource.FromFile(string filePath, string? password = null);

// URI/URL
var source = PdfSource.FromUri(Uri uri, string? password = null);

// Stream
var source = PdfSource.FromStream(Stream stream, string? password = null);

// Byte array
var source = PdfSource.FromBytes(byte[] data, string? password = null);

// Asset/Resource
var source = PdfSource.FromAsset(string assetName, string? password = null);

Implicit Conversion (Convenient):

// String to PdfSource - auto-detects type
PdfSource source = "https://example.com/doc.pdf";  // → UriPdfSource
PdfSource source = "sample.pdf";                    // → AssetPdfSource
PdfSource source = "/path/to/file.pdf";             // → FilePdfSource

// Uri to PdfSource
PdfSource source = new Uri("https://example.com/doc.pdf");

String Conversion Rules: | Pattern | Result | |---------|--------| | http://... or https://... | UriPdfSource | | asset://filename.pdf | AssetPdfSource | | file:///path/to/file.pdf | FilePdfSource | | filename.pdf (no path separators) | AssetPdfSource | | /path/to/file.pdf (rooted path) | FilePdfSource |

Enums

// How pages fit on screen
public enum FitPolicy
{
    Width,    // Fit to width
    Height,   // Fit to height
    Both      // Fit both dimensions
}

// Page display mode
public enum PdfDisplayMode
{
    SinglePage,           // One page at a time
    SinglePageContinuous  // Continuous scrolling
}

// Scroll direction
public enum PdfScrollOrientation
{
    Vertical,    // Scroll up/down
    Horizontal   // Scroll left/right
}

Methods

// Navigate to specific page (0-based)
pdfViewer.GoToPage(int pageIndex);

// Reload current document
pdfViewer.Reload();

💡 Examples

Complete PDF Viewer

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:pdf="clr-namespace:MauiNativePdfView;assembly=MauiNativePdfView"
             x:Class="MyApp.PdfPage">

    <Grid RowDefinitions="Auto,*,Auto">

        
        <HorizontalStackLayout Grid.Row="0" Padding="10" Spacing="10">
            <Button Text="◀" Clicked="OnPreviousPage" />
            <Button Text="▶" Clicked="OnNextPage" />
            <Button Text="Zoom In" Clicked="OnZoomIn" />
            <Button Text="Zoom Out" Clicked="OnZoomOut" />
        </HorizontalStackLayout>

        
        <pdf:PdfView x:Name="pdfViewer"
                     Grid.Row="1"
                     EnableZoom="True"
                     EnableSwipe="True"
                     EnableLinkNavigation="True"
                     EnableAnnotationRendering="True"
                     PageSpacing="10"
                     BackgroundColor="#F5F5F5"
                     DocumentLoaded="OnDocumentLoaded"
                     PageChanged="OnPageChanged"
                     LinkTapped="OnLinkTapped"
                     Error="OnError" />

        
        <Label x:Name="statusLabel"
               Grid.Row="2"
               Padding="10"
               HorizontalOptions="Center" />
    </Grid>

</ContentPage>
public partial class PdfPage : ContentPage
{
    public PdfPage()
    {
        InitializeComponent();
        pdfViewer.Source = PdfSource.FromAsset("sample.pdf");
    }

    private void OnDocumentLoaded(object sender, DocumentLoadedEventArgs e)
    {
        statusLabel.Text = $"Loaded: {e.PageCount} pages - {e.Title}";
    }

    private void OnPageChanged(object sender, PageChangedEventArgs e)
    {
        statusLabel.Text = $"Page {e.PageIndex + 1} of {e.PageCount}";
    }

    private void OnLinkTapped(object sender, LinkTappedEventArgs e)
    {
        if (e.Uri != null)
        {
            // Intercept and handle external link yourself
            DisplayAlert("Link Tapped", $"Opening: {e.Uri}", "OK");
            Launcher.OpenAsync(e.Uri);

            // Prevent default navigation
            e.Handled = true;
        }
        else if (e.DestinationPage.HasValue)
        {
            // Internal link - allow default navigation
            // Or set e.Handled = true to prevent it
        }
    }

    private void OnError(object sender, PdfErrorEventArgs e)
    {
        DisplayAlert("Error", e.Message, "OK");
    }

    private void OnPreviousPage(object sender, EventArgs e)
    {
        if (pdfViewer.CurrentPage > 0)
            pdfViewer.GoToPage(pdfViewer.CurrentPage - 1);
    }

    private void OnNextPage(object sender, EventArgs e)
    {
        if (pdfViewer.CurrentPage < pdfViewer.PageCount - 1)
            pdfViewer.GoToPage(pdfViewer.CurrentPage + 1);
    }

    private void OnZoomIn(object sender, EventArgs e)
    {
        pdfViewer.Zoom = Math.Min(pdfViewer.Zoom + 0.5f, pdfViewer.MaxZoom);
    }

    private void OnZoomOut(object sender, EventArgs e)
    {
        pdfViewer.Zoom = Math.Max(pdfViewer.Zoom - 0.5f, pdfViewer.MinZoom);
    }
}

MVVM Pattern

public class PdfViewModel : INotifyPropertyChanged
{
    private PdfSource _pdfSource;
    private int _currentPage;
    private int _pageCount;

    public PdfSource PdfSource
    {
        get => _pdfSource;
        set => SetProperty(ref _pdfSource, value);
    }

    public int CurrentPage
    {
        get => _currentPage;
        set => SetProperty(ref _currentPage, value);
    }

    public int PageCount
    {
        get => _pageCount;
        set => SetProperty(ref _pageCount, value);
    }

    public Command LoadPdfCommand { get; }
    public Command<int> GoToPageCommand { get; }

    public PdfViewModel()
    {
        LoadPdfCommand = new Command(LoadPdf);
        GoToPageCommand = new Command<int>(GoToPage);
    }

    private void LoadPdf()
    {
        PdfSource = PdfSource.FromAsset("document.pdf");
    }

    private void GoToPage(int pageIndex)
    {
        // Page navigation handled by binding
    }
}
<pdf:PdfView Source="{Binding PdfSource}"
             CurrentPage="{Binding CurrentPage}"
             PageCount="{Binding PageCount}" />

Password-Protected PDFs

try
{
    pdfViewer.Source = PdfSource.FromFile("encrypted.pdf", "mypassword");
}
catch (Exception ex)
{
    // Handle incorrect password
    await DisplayAlert("Error", "Invalid password", "OK");
}

Both iOS and Android support intercepting link taps before navigation occurs. This allows you to handle links yourself or prevent navigation entirely.

pdfViewer.LinkTapped += (sender, e) =>
{
    Console.WriteLine($"Link tapped: {e.Uri}");

    if (e.Uri?.Contains("example.com") == true)
    {
        // Custom handling for specific domain
        DisplayAlert("Info", "This link is not allowed", "OK");
        e.Handled = true; // Prevent navigation
    }
    else if (e.Uri != null)
    {
        // Log analytics before opening
        Analytics.TrackEvent("PDF_Link_Clicked", new Dictionary<string, string>
        {
            { "Uri", e.Uri }
        });

        // Allow default navigation (or handle manually)
        e.Handled = false;
    }
};

Platform Implementation:

  • iOS: Uses PdfViewDelegate.WillClickOnLink to intercept before navigation
  • Android: Uses LinkHandler.HandleLinkEvent to intercept before navigation

Tap Gesture Handling

Enable custom tap detection with page coordinates:

pdfViewer.EnableTapGestures = true;

pdfViewer.Tapped += (sender, e) =>
{
    Console.WriteLine($"Tapped page {e.PageIndex} at ({e.X}, {e.Y})");

    // Add your custom tap handling logic
    // For example: show a custom menu, add annotations, etc.
};

Note: When EnableTapGestures = false (default), the PDF viewer uses native platform tap handling which is optimized for link detection.

Annotation Handling (iOS)

private void OnAnnotationTapped(object sender, AnnotationTappedEventArgs e)
{
    Console.WriteLine($"Annotation on page {e.PageIndex + 1}");
    Console.WriteLine($"Type: {e.AnnotationType}");
    Console.WriteLine($"Contents: {e.Contents}");
    Console.WriteLine($"Bounds: {e.Bounds}");

    // Prevent default behavior if needed
    e.Handled = true;
}

Note: Annotation tap detection is only supported on iOS with PDFKit. Android's AhmerPdfium library does not expose annotation tap events.

🎯 Common Scenarios

Restrict External Navigation

pdfViewer.LinkTapped += (sender, e) =>
{
    if (e.Uri != null && e.Uri.StartsWith("http"))
    {
        DisplayAlert("Restricted", "External links are not allowed", "OK");
        e.Handled = true; // Block navigation
    }
};
pdfViewer.LinkTapped += (sender, e) =>
{
    // Log the link click
    Analytics.TrackEvent("PDF_Link_Clicked", new Dictionary<string, string>
    {
        { "Document", pdfViewer.Source?.ToString() ?? "Unknown" },
        { "Link", e.Uri ?? $"Page {e.DestinationPage}" },
        { "CurrentPage", pdfViewer.CurrentPage.ToString() }
    });

    // Allow normal navigation
    e.Handled = false;
};
pdfViewer.LinkTapped += async (sender, e) =>
{
    if (e.Uri != null)
    {
        var result = await DisplayAlert(
            "Open Link?",
            $"Do you want to open {e.Uri}?",
            "Yes",
            "No"
        );

        if (result)
        {
            await Launcher.OpenAsync(e.Uri);
        }

        e.Handled = true; // Prevent default navigation
    }
};
pdfViewer.LinkTapped += async (sender, e) =>
{
    if (e.Uri?.StartsWith("myapp://") == true)
    {
        // Handle custom URL scheme
        await Shell.Current.GoToAsync(e.Uri.Replace("myapp://", ""));
        e.Handled = true;
    }
};
// Simple approach
pdfViewer.EnableLinkNavigation = false;

// Or intercept all links
pdfViewer.LinkTapped += (sender, e) =>
{
    e.Handled = true; // Block all navigation
};

🏗️ Architecture

┌─────────────────────────────────────┐
│     .NET MAUI Application           │
└─────────────┬───────────────────────┘
              │
┌─────────────▼───────────────────────┐
│      MauiNativePdfView              │
│  ┌────────────────────────────────┐ │
│  │   PdfView (MAUI Control)       │ │
│  │   - Bindable Properties        │ │
│  │   - Event Handlers             │ │
│  │   - Platform Handlers          │ │
│  └────────────────────────────────┘ │
└─────────────┬───────────────────────┘
              │
      ┌───────┴────────┐
      │                │
┌─────▼──────┐   ┌────▼───────┐
│  Android   │   │    iOS     │
│  Handler   │   │  Handler   │
└─────┬──────┘   └────┬───────┘
      │               │
┌─────▼──────┐   ┌────▼───────┐
│AhmerPdfium │   │  PDFKit    │
│ (Native)   │   │ (Native)   │
└────────────┘   └────────────┘

🔧 Platform Details

iOS (PDFKit)

  • Framework: Apple's native PDFKit
  • Version: iOS 12.2+
  • Features: Full annotation support, smooth rendering, link interception via PdfViewDelegate
  • Link Handling: Native WillClickOnLink delegate method
  • Size: 0 KB (system framework)

Android (AhmerPdfium)

  • Library: AhmerPdfium by Ahmer Afzal
  • Base: Enhanced fork of AndroidPdfViewer
  • Version: 2.0.1 (viewer) + 1.9.2 (pdfium)
  • Features: 16KB page size support, reliable rendering, link interception via LinkHandler
  • Link Handling: Custom ILinkHandler implementation
  • Size: ~16MB (native libraries for all architectures)
  • Note: Annotation tap events not supported by library

📊 Performance

  • Memory Efficient: Native rendering engines handle memory management
  • Fast Loading: Platform-optimized PDF parsing
  • Smooth Scrolling: Hardware-accelerated rendering
  • Large Files: Tested with 100+ page documents

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Building from Source

git clone https://github.com/TheEightBot/MauiNativePdfView.git
cd MauiNativePdfView
dotnet restore
dotnet build

Running Tests

cd samples/MauiPdfViewerSample
dotnet build

❓ Troubleshooting

If links are not responding on iOS, ensure:

  1. EnableLinkNavigation = true (default)
  2. The PDF actually contains link annotations
  3. You're not setting e.Handled = true for all links in the LinkTapped event

Tapped Event Not Firing

The Tapped event requires:

pdfViewer.EnableTapGestures = true;

Note: When EnableTapGestures = true, it may interfere with native link handling on some platforms. For link detection only, keep it false (default) and use the LinkTapped event.

LinkTapped Event Handler Not Called

Ensure you're subscribing to the event:

pdfViewer.LinkTapped += OnLinkTapped;

Or in XAML:

<pdf:PdfView LinkTapped="OnLinkTapped" />

Android Annotation Events

Annotation tap events (AnnotationTapped) are only supported on iOS. The Android AhmerPdfium library does not expose annotation-level tap detection. Use the Tapped event as an alternative for Android.

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

Third-Party Licenses

  • AhmerPdfium: Apache License 2.0
  • PDFKit: Apple System Framework

🙏 Acknowledgments

  • Ahmer Afzal - AhmerPdfium library maintainer
  • barteksc - Original AndroidPdfViewer
  • Apple - PDFKit framework
  • .NET MAUI Team - Excellent framework

💬 Support

⭐ Show Your Support

If you find this library helpful, please give it a star! ⭐


<div align="center">

Made with ❤️ for the .NET MAUI community

⬆ Back to Top

</div>

Product Compatible and additional computed target framework versions.
.NET net9.0-android35.0 is compatible.  net9.0-ios18.0 is compatible.  net10.0-android was computed.  net10.0-ios was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.7 109 1/9/2026
1.0.6 276 12/5/2025
1.0.5 184 11/26/2025
1.0.4 185 11/25/2025