{"id":36646634,"url":"https://github.com/theeightbot/mauinativepdfview","last_synced_at":"2026-01-12T10:01:01.147Z","repository":{"id":326104676,"uuid":"1103965235","full_name":"TheEightBot/MauiNativePdfView","owner":"TheEightBot","description":"A high-performance, cross-platform PDF viewer for .NET MAUI","archived":false,"fork":false,"pushed_at":"2025-12-05T14:16:38.000Z","size":28367,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-26T16:19:29.521Z","etag":null,"topics":["dotnet-maui","dotnet-maui-controls","maui","maui-apps"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TheEightBot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-25T15:13:18.000Z","updated_at":"2025-12-20T02:01:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/TheEightBot/MauiNativePdfView","commit_stats":null,"previous_names":["theeightbot/mauinativepdfview"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/TheEightBot/MauiNativePdfView","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheEightBot%2FMauiNativePdfView","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheEightBot%2FMauiNativePdfView/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheEightBot%2FMauiNativePdfView/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheEightBot%2FMauiNativePdfView/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TheEightBot","download_url":"https://codeload.github.com/TheEightBot/MauiNativePdfView/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheEightBot%2FMauiNativePdfView/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28337870,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T06:09:07.588Z","status":"ssl_error","status_checked_at":"2026-01-12T06:05:18.301Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dotnet-maui","dotnet-maui-controls","maui","maui-apps"],"created_at":"2026-01-12T10:00:50.280Z","updated_at":"2026-01-12T10:01:01.139Z","avatar_url":"https://github.com/TheEightBot.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# MauiNativePdfView\n\n\u003cimg src=\"images/icon.svg\" width=\"200\" /\u003e\n\n### A high-performance, cross-platform PDF viewer for .NET MAUI\n\n[![NuGet](https://img.shields.io/nuget/v/Eightbot.MauiNativePdfView.svg)](https://www.nuget.org/packages/EightBot.MauiNativePdfView/)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Eightbot.MauiNativePdfView.svg)](https://www.nuget.org/packages/EightBot.MauiNativePdfView/)\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![.NET](https://img.shields.io/badge/.NET-9.0-purple.svg)](https://dotnet.microsoft.com/download)\n[![MAUI](https://img.shields.io/badge/MAUI-Latest-green.svg)](https://github.com/dotnet/maui)\n\n**Native PDF rendering** • **Zero web dependencies** • **Full feature parity**\n\n[Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [Documentation](#-documentation) • [Examples](#-examples)\n\n\u003c/div\u003e\n\n---\n\n## 🎯 Overview\n\nMauiNativePdfView 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.\n\n### Why MauiNativePdfView?\n\n- **🚀 Native Performance** - Uses PDFKit (iOS) and AhmerPdfium (Android) for optimal speed\n- **💪 Feature Complete** - Comprehensive API with zoom, navigation, annotations, and events\n- **🎨 Consistent API** - Write once, works on both iOS and Android\n- **📦 Zero Web Dependencies** - No WebView, no JavaScript, pure native rendering\n- **🔧 Highly Configurable** - Extensive properties for customization\n- **📱 Production Ready** - Battle-tested with full annotation support\n\n## ✨ Features\n\n### Core Functionality\n\n- ✅ **Multiple PDF Sources** - Load from files, URLs, streams, byte arrays, or embedded assets\n- ✅ **Password Protection** - Full support for encrypted PDFs\n- ✅ **Zoom \u0026 Gestures** - Pinch-to-zoom, double-tap zoom, with configurable min/max levels\n- ✅ **Page Navigation** - Swipe between pages, programmatic navigation, page events\n- ✅ **Link Interception** - Intercept and handle link taps before navigation (both platforms)\n- ✅ **Link Handling** - Automatic detection and handling of internal/external links\n- ✅ **Display Modes** - Single page or continuous scrolling\n- ✅ **Scroll Orientation** - Vertical or horizontal page layout\n\n### Advanced Features\n\n- ✅ **Annotation Rendering** - Toggle PDF annotations on/off\n- ✅ **Annotation Events** - Tap detection with annotation details (iOS)\n- ✅ **Tap Gesture Control** - Enable/disable custom tap interception\n- ✅ **Quality Control** - Antialiasing and rendering quality settings\n- ✅ **Background Color** - Customizable viewer background\n- ✅ **Page Spacing** - Adjustable spacing between pages\n- ✅ **Event System** - Comprehensive events for document lifecycle\n\n### Events\n\n- `DocumentLoaded` - Fires when PDF is loaded with page count and metadata\n- `PageChanged` - Current page and total page count updates\n- `LinkTapped` - Intercept link taps before navigation (set `e.Handled = true` to prevent)\n- `Tapped` - General tap events with page coordinates (requires `EnableTapGestures = true`)\n- `AnnotationTapped` - Annotation tap with type, content, and bounds (iOS)\n- `Rendered` - Initial rendering complete\n- `Error` - Error handling with detailed messages\n\n## 📦 Installation\n\n### NuGet Package Manager\n\n```bash\ndotnet add package MauiNativePdfView\n```\n\n### Package Manager Console\n\n```powershell\nInstall-Package MauiNativePdfView\n```\n\n### Requirements\n\n- **.NET 9.0** or later\n- **iOS 12.2+** (PDFKit)\n- **Android 7.0+** (API 24+)\n\n## 🚀 Quick Start\n\n### 1. Add Namespace\n\n**Option A: Custom Schema (Recommended)**\n\n```xml\nxmlns:pdf=\"http://eightbot.com/maui/pdfview\"\n```\n\n**Option B: CLR Namespace**\n\n```xml\nxmlns:pdf=\"clr-namespace:MauiNativePdfView;assembly=MauiNativePdfView\"\n```\n\n### 2. Basic XAML\n\n```xml\n\u003c!-- Simple string binding - auto-converts to PdfSource! --\u003e\n\u003cpdf:PdfView Source=\"https://example.com/document.pdf\" /\u003e\n\n\u003c!-- Or with full control --\u003e\n\u003cpdf:PdfView x:Name=\"pdfViewer\"\n             EnableZoom=\"True\"\n             EnableSwipe=\"True\"\n             DocumentLoaded=\"OnDocumentLoaded\"\n             PageChanged=\"OnPageChanged\" /\u003e\n```\n\n### String to PdfSource Conversion\n\nThe library supports automatic string conversion in XAML:\n\n```xml\n\u003c!-- URL - automatically becomes UriPdfSource --\u003e\n\u003cpdf:PdfView Source=\"https://example.com/document.pdf\" /\u003e\n\n\u003c!-- Asset - simple filename becomes AssetPdfSource --\u003e\n\u003cpdf:PdfView Source=\"sample.pdf\" /\u003e\n\n\u003c!-- Asset with explicit prefix --\u003e\n\u003cpdf:PdfView Source=\"asset://documents/guide.pdf\" /\u003e\n\n\u003c!-- File URI --\u003e\n\u003cpdf:PdfView Source=\"file:///path/to/document.pdf\" /\u003e\n```\n\n### 3. Load a PDF (Code-Behind)\n\n```csharp\n// From file\npdfViewer.Source = PdfSource.FromFile(\"/path/to/document.pdf\");\n\n// From URL\npdfViewer.Source = PdfSource.FromUri(new Uri(\"https://example.com/doc.pdf\"));\n\n// From embedded asset\npdfViewer.Source = PdfSource.FromAsset(\"sample.pdf\");\n\n// From stream\npdfViewer.Source = PdfSource.FromStream(myStream);\n\n// From byte array\npdfViewer.Source = PdfSource.FromBytes(pdfBytes);\n\n// With password\npdfViewer.Source = PdfSource.FromFile(\"/path/to/encrypted.pdf\", \"password\");\n```\n\n### 4. Handle Events\n\n```csharp\nprivate void OnDocumentLoaded(object sender, DocumentLoadedEventArgs e)\n{\n    Console.WriteLine($\"Loaded: {e.PageCount} pages\");\n    Console.WriteLine($\"Title: {e.Title}\");\n    Console.WriteLine($\"Author: {e.Author}\");\n}\n\nprivate void OnPageChanged(object sender, PageChangedEventArgs e)\n{\n    Console.WriteLine($\"Page {e.PageIndex + 1} of {e.PageCount}\");\n}\n```\n\n## 📖 Documentation\n\n### PdfView Properties\n\n| Property                    | Type                   | Default                | Description                    |\n| --------------------------- | ---------------------- | ---------------------- | ------------------------------ |\n| `Source`                    | `PdfSource`            | `null`                 | PDF source to display          |\n| `EnableZoom`                | `bool`                 | `true`                 | Enable pinch-to-zoom           |\n| `EnableSwipe`               | `bool`                 | `true`                 | Enable swipe gestures          |\n| `EnableTapGestures`         | `bool`                 | `false`                | Enable tap interception        |\n| `EnableLinkNavigation`      | `bool`                 | `true`                 | Enable clickable links         |\n| `Zoom`                      | `float`                | `1.0f`                 | Current zoom level             |\n| `MinZoom`                   | `float`                | `1.0f`                 | Minimum zoom level             |\n| `MaxZoom`                   | `float`                | `3.0f`                 | Maximum zoom level             |\n| `PageSpacing`               | `int`                  | `10`                   | Spacing between pages (pixels) |\n| `FitPolicy`                 | `FitPolicy`            | `Width`                | How pages fit on screen        |\n| `DisplayMode`               | `PdfDisplayMode`       | `SinglePageContinuous` | Page display mode              |\n| `ScrollOrientation`         | `PdfScrollOrientation` | `Vertical`             | Scroll direction               |\n| `DefaultPage`               | `int`                  | `0`                    | Initial page (0-based)         |\n| `EnableAntialiasing`        | `bool`                 | `true`                 | Antialiasing (Android only)    |\n| `UseBestQuality`            | `bool`                 | `true`                 | Best quality rendering         |\n| `BackgroundColor`           | `Color`                | `null`                 | Viewer background color        |\n| `EnableAnnotationRendering` | `bool`                 | `true`                 | Show PDF annotations           |\n| `CurrentPage`               | `int`                  | `0`                    | Current page (readonly)        |\n| `PageCount`                 | `int`                  | `0`                    | Total pages (readonly)         |\n\n### PdfSource Types\n\nThe `PdfSource` class supports automatic string conversion via implicit operators and TypeConverter, making it easy to use in both XAML and code.\n\n**Factory Methods (Code-Behind):**\n\n```csharp\n// File path\nvar source = PdfSource.FromFile(string filePath, string? password = null);\n\n// URI/URL\nvar source = PdfSource.FromUri(Uri uri, string? password = null);\n\n// Stream\nvar source = PdfSource.FromStream(Stream stream, string? password = null);\n\n// Byte array\nvar source = PdfSource.FromBytes(byte[] data, string? password = null);\n\n// Asset/Resource\nvar source = PdfSource.FromAsset(string assetName, string? password = null);\n```\n\n**Implicit Conversion (Convenient):**\n\n```csharp\n// String to PdfSource - auto-detects type\nPdfSource source = \"https://example.com/doc.pdf\";  // → UriPdfSource\nPdfSource source = \"sample.pdf\";                    // → AssetPdfSource\nPdfSource source = \"/path/to/file.pdf\";             // → FilePdfSource\n\n// Uri to PdfSource\nPdfSource source = new Uri(\"https://example.com/doc.pdf\");\n```\n\n**String Conversion Rules:**\n| Pattern | Result |\n|---------|--------|\n| `http://...` or `https://...` | `UriPdfSource` |\n| `asset://filename.pdf` | `AssetPdfSource` |\n| `file:///path/to/file.pdf` | `FilePdfSource` |\n| `filename.pdf` (no path separators) | `AssetPdfSource` |\n| `/path/to/file.pdf` (rooted path) | `FilePdfSource` |\n\n### Enums\n\n```csharp\n// How pages fit on screen\npublic enum FitPolicy\n{\n    Width,    // Fit to width\n    Height,   // Fit to height\n    Both      // Fit both dimensions\n}\n\n// Page display mode\npublic enum PdfDisplayMode\n{\n    SinglePage,           // One page at a time\n    SinglePageContinuous  // Continuous scrolling\n}\n\n// Scroll direction\npublic enum PdfScrollOrientation\n{\n    Vertical,    // Scroll up/down\n    Horizontal   // Scroll left/right\n}\n```\n\n### Methods\n\n```csharp\n// Navigate to specific page (0-based)\npdfViewer.GoToPage(int pageIndex);\n\n// Reload current document\npdfViewer.Reload();\n```\n\n## 💡 Examples\n\n### Complete PDF Viewer\n\n```xml\n\u003cContentPage xmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\n             xmlns:pdf=\"clr-namespace:MauiNativePdfView;assembly=MauiNativePdfView\"\n             x:Class=\"MyApp.PdfPage\"\u003e\n\n    \u003cGrid RowDefinitions=\"Auto,*,Auto\"\u003e\n\n        \u003c!-- Toolbar --\u003e\n        \u003cHorizontalStackLayout Grid.Row=\"0\" Padding=\"10\" Spacing=\"10\"\u003e\n            \u003cButton Text=\"◀\" Clicked=\"OnPreviousPage\" /\u003e\n            \u003cButton Text=\"▶\" Clicked=\"OnNextPage\" /\u003e\n            \u003cButton Text=\"Zoom In\" Clicked=\"OnZoomIn\" /\u003e\n            \u003cButton Text=\"Zoom Out\" Clicked=\"OnZoomOut\" /\u003e\n        \u003c/HorizontalStackLayout\u003e\n\n        \u003c!-- PDF Viewer --\u003e\n        \u003cpdf:PdfView x:Name=\"pdfViewer\"\n                     Grid.Row=\"1\"\n                     EnableZoom=\"True\"\n                     EnableSwipe=\"True\"\n                     EnableLinkNavigation=\"True\"\n                     EnableAnnotationRendering=\"True\"\n                     PageSpacing=\"10\"\n                     BackgroundColor=\"#F5F5F5\"\n                     DocumentLoaded=\"OnDocumentLoaded\"\n                     PageChanged=\"OnPageChanged\"\n                     LinkTapped=\"OnLinkTapped\"\n                     Error=\"OnError\" /\u003e\n\n        \u003c!-- Status Bar --\u003e\n        \u003cLabel x:Name=\"statusLabel\"\n               Grid.Row=\"2\"\n               Padding=\"10\"\n               HorizontalOptions=\"Center\" /\u003e\n    \u003c/Grid\u003e\n\n\u003c/ContentPage\u003e\n```\n\n```csharp\npublic partial class PdfPage : ContentPage\n{\n    public PdfPage()\n    {\n        InitializeComponent();\n        pdfViewer.Source = PdfSource.FromAsset(\"sample.pdf\");\n    }\n\n    private void OnDocumentLoaded(object sender, DocumentLoadedEventArgs e)\n    {\n        statusLabel.Text = $\"Loaded: {e.PageCount} pages - {e.Title}\";\n    }\n\n    private void OnPageChanged(object sender, PageChangedEventArgs e)\n    {\n        statusLabel.Text = $\"Page {e.PageIndex + 1} of {e.PageCount}\";\n    }\n\n    private void OnLinkTapped(object sender, LinkTappedEventArgs e)\n    {\n        if (e.Uri != null)\n        {\n            // Intercept and handle external link yourself\n            DisplayAlert(\"Link Tapped\", $\"Opening: {e.Uri}\", \"OK\");\n            Launcher.OpenAsync(e.Uri);\n\n            // Prevent default navigation\n            e.Handled = true;\n        }\n        else if (e.DestinationPage.HasValue)\n        {\n            // Internal link - allow default navigation\n            // Or set e.Handled = true to prevent it\n        }\n    }\n\n    private void OnError(object sender, PdfErrorEventArgs e)\n    {\n        DisplayAlert(\"Error\", e.Message, \"OK\");\n    }\n\n    private void OnPreviousPage(object sender, EventArgs e)\n    {\n        if (pdfViewer.CurrentPage \u003e 0)\n            pdfViewer.GoToPage(pdfViewer.CurrentPage - 1);\n    }\n\n    private void OnNextPage(object sender, EventArgs e)\n    {\n        if (pdfViewer.CurrentPage \u003c pdfViewer.PageCount - 1)\n            pdfViewer.GoToPage(pdfViewer.CurrentPage + 1);\n    }\n\n    private void OnZoomIn(object sender, EventArgs e)\n    {\n        pdfViewer.Zoom = Math.Min(pdfViewer.Zoom + 0.5f, pdfViewer.MaxZoom);\n    }\n\n    private void OnZoomOut(object sender, EventArgs e)\n    {\n        pdfViewer.Zoom = Math.Max(pdfViewer.Zoom - 0.5f, pdfViewer.MinZoom);\n    }\n}\n```\n\n### MVVM Pattern\n\n```csharp\npublic class PdfViewModel : INotifyPropertyChanged\n{\n    private PdfSource _pdfSource;\n    private int _currentPage;\n    private int _pageCount;\n\n    public PdfSource PdfSource\n    {\n        get =\u003e _pdfSource;\n        set =\u003e SetProperty(ref _pdfSource, value);\n    }\n\n    public int CurrentPage\n    {\n        get =\u003e _currentPage;\n        set =\u003e SetProperty(ref _currentPage, value);\n    }\n\n    public int PageCount\n    {\n        get =\u003e _pageCount;\n        set =\u003e SetProperty(ref _pageCount, value);\n    }\n\n    public Command LoadPdfCommand { get; }\n    public Command\u003cint\u003e GoToPageCommand { get; }\n\n    public PdfViewModel()\n    {\n        LoadPdfCommand = new Command(LoadPdf);\n        GoToPageCommand = new Command\u003cint\u003e(GoToPage);\n    }\n\n    private void LoadPdf()\n    {\n        PdfSource = PdfSource.FromAsset(\"document.pdf\");\n    }\n\n    private void GoToPage(int pageIndex)\n    {\n        // Page navigation handled by binding\n    }\n}\n```\n\n```xml\n\u003cpdf:PdfView Source=\"{Binding PdfSource}\"\n             CurrentPage=\"{Binding CurrentPage}\"\n             PageCount=\"{Binding PageCount}\" /\u003e\n```\n\n### Password-Protected PDFs\n\n```csharp\ntry\n{\n    pdfViewer.Source = PdfSource.FromFile(\"encrypted.pdf\", \"mypassword\");\n}\ncatch (Exception ex)\n{\n    // Handle incorrect password\n    await DisplayAlert(\"Error\", \"Invalid password\", \"OK\");\n}\n```\n\n### Link Interception\n\nBoth iOS and Android support intercepting link taps before navigation occurs. This allows you to handle links yourself or prevent navigation entirely.\n\n```csharp\npdfViewer.LinkTapped += (sender, e) =\u003e\n{\n    Console.WriteLine($\"Link tapped: {e.Uri}\");\n\n    if (e.Uri?.Contains(\"example.com\") == true)\n    {\n        // Custom handling for specific domain\n        DisplayAlert(\"Info\", \"This link is not allowed\", \"OK\");\n        e.Handled = true; // Prevent navigation\n    }\n    else if (e.Uri != null)\n    {\n        // Log analytics before opening\n        Analytics.TrackEvent(\"PDF_Link_Clicked\", new Dictionary\u003cstring, string\u003e\n        {\n            { \"Uri\", e.Uri }\n        });\n\n        // Allow default navigation (or handle manually)\n        e.Handled = false;\n    }\n};\n```\n\n**Platform Implementation:**\n\n- **iOS**: Uses `PdfViewDelegate.WillClickOnLink` to intercept before navigation\n- **Android**: Uses `LinkHandler.HandleLinkEvent` to intercept before navigation\n\n### Tap Gesture Handling\n\nEnable custom tap detection with page coordinates:\n\n```csharp\npdfViewer.EnableTapGestures = true;\n\npdfViewer.Tapped += (sender, e) =\u003e\n{\n    Console.WriteLine($\"Tapped page {e.PageIndex} at ({e.X}, {e.Y})\");\n\n    // Add your custom tap handling logic\n    // For example: show a custom menu, add annotations, etc.\n};\n```\n\n**Note**: When `EnableTapGestures = false` (default), the PDF viewer uses native platform tap handling which is optimized for link detection.\n\n### Annotation Handling (iOS)\n\n```csharp\nprivate void OnAnnotationTapped(object sender, AnnotationTappedEventArgs e)\n{\n    Console.WriteLine($\"Annotation on page {e.PageIndex + 1}\");\n    Console.WriteLine($\"Type: {e.AnnotationType}\");\n    Console.WriteLine($\"Contents: {e.Contents}\");\n    Console.WriteLine($\"Bounds: {e.Bounds}\");\n\n    // Prevent default behavior if needed\n    e.Handled = true;\n}\n```\n\n**Note**: Annotation tap detection is only supported on iOS with PDFKit. Android's AhmerPdfium library does not expose annotation tap events.\n\n## 🎯 Common Scenarios\n\n### Restrict External Navigation\n\n```csharp\npdfViewer.LinkTapped += (sender, e) =\u003e\n{\n    if (e.Uri != null \u0026\u0026 e.Uri.StartsWith(\"http\"))\n    {\n        DisplayAlert(\"Restricted\", \"External links are not allowed\", \"OK\");\n        e.Handled = true; // Block navigation\n    }\n};\n```\n\n### Track Link Clicks for Analytics\n\n```csharp\npdfViewer.LinkTapped += (sender, e) =\u003e\n{\n    // Log the link click\n    Analytics.TrackEvent(\"PDF_Link_Clicked\", new Dictionary\u003cstring, string\u003e\n    {\n        { \"Document\", pdfViewer.Source?.ToString() ?? \"Unknown\" },\n        { \"Link\", e.Uri ?? $\"Page {e.DestinationPage}\" },\n        { \"CurrentPage\", pdfViewer.CurrentPage.ToString() }\n    });\n\n    // Allow normal navigation\n    e.Handled = false;\n};\n```\n\n### Custom Link Handling with Confirmation\n\n```csharp\npdfViewer.LinkTapped += async (sender, e) =\u003e\n{\n    if (e.Uri != null)\n    {\n        var result = await DisplayAlert(\n            \"Open Link?\",\n            $\"Do you want to open {e.Uri}?\",\n            \"Yes\",\n            \"No\"\n        );\n\n        if (result)\n        {\n            await Launcher.OpenAsync(e.Uri);\n        }\n\n        e.Handled = true; // Prevent default navigation\n    }\n};\n```\n\n### Deep Link Handling\n\n```csharp\npdfViewer.LinkTapped += async (sender, e) =\u003e\n{\n    if (e.Uri?.StartsWith(\"myapp://\") == true)\n    {\n        // Handle custom URL scheme\n        await Shell.Current.GoToAsync(e.Uri.Replace(\"myapp://\", \"\"));\n        e.Handled = true;\n    }\n};\n```\n\n### Disable All Link Navigation\n\n```csharp\n// Simple approach\npdfViewer.EnableLinkNavigation = false;\n\n// Or intercept all links\npdfViewer.LinkTapped += (sender, e) =\u003e\n{\n    e.Handled = true; // Block all navigation\n};\n```\n\n## 🏗️ Architecture\n\n```\n┌─────────────────────────────────────┐\n│     .NET MAUI Application           │\n└─────────────┬───────────────────────┘\n              │\n┌─────────────▼───────────────────────┐\n│      MauiNativePdfView              │\n│  ┌────────────────────────────────┐ │\n│  │   PdfView (MAUI Control)       │ │\n│  │   - Bindable Properties        │ │\n│  │   - Event Handlers             │ │\n│  │   - Platform Handlers          │ │\n│  └────────────────────────────────┘ │\n└─────────────┬───────────────────────┘\n              │\n      ┌───────┴────────┐\n      │                │\n┌─────▼──────┐   ┌────▼───────┐\n│  Android   │   │    iOS     │\n│  Handler   │   │  Handler   │\n└─────┬──────┘   └────┬───────┘\n      │               │\n┌─────▼──────┐   ┌────▼───────┐\n│AhmerPdfium │   │  PDFKit    │\n│ (Native)   │   │ (Native)   │\n└────────────┘   └────────────┘\n```\n\n## 🔧 Platform Details\n\n### iOS (PDFKit)\n\n- **Framework**: Apple's native PDFKit\n- **Version**: iOS 12.2+\n- **Features**: Full annotation support, smooth rendering, link interception via `PdfViewDelegate`\n- **Link Handling**: Native `WillClickOnLink` delegate method\n- **Size**: 0 KB (system framework)\n\n### Android (AhmerPdfium)\n\n- **Library**: [AhmerPdfium](https://github.com/AhmerAfzal1/AhmerPdfium) by Ahmer Afzal\n- **Base**: Enhanced fork of [AndroidPdfViewer](https://github.com/barteksc/AndroidPdfViewer)\n- **Version**: 2.0.1 (viewer) + 1.9.2 (pdfium)\n- **Features**: 16KB page size support, reliable rendering, link interception via `LinkHandler`\n- **Link Handling**: Custom `ILinkHandler` implementation\n- **Size**: ~16MB (native libraries for all architectures)\n- **Note**: Annotation tap events not supported by library\n\n## 📊 Performance\n\n- **Memory Efficient**: Native rendering engines handle memory management\n- **Fast Loading**: Platform-optimized PDF parsing\n- **Smooth Scrolling**: Hardware-accelerated rendering\n- **Large Files**: Tested with 100+ page documents\n\n## 🤝 Contributing\n\nContributions 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.\n\n### Building from Source\n\n```bash\ngit clone https://github.com/TheEightBot/MauiNativePdfView.git\ncd MauiNativePdfView\ndotnet restore\ndotnet build\n```\n\n### Running Tests\n\n```bash\ncd samples/MauiPdfViewerSample\ndotnet build\n```\n\n## ❓ Troubleshooting\n\n### Links Not Working on iOS\n\nIf links are not responding on iOS, ensure:\n\n1. `EnableLinkNavigation = true` (default)\n2. The PDF actually contains link annotations\n3. You're not setting `e.Handled = true` for all links in the `LinkTapped` event\n\n### Tapped Event Not Firing\n\nThe `Tapped` event requires:\n\n```csharp\npdfViewer.EnableTapGestures = true;\n```\n\n**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.\n\n### LinkTapped Event Handler Not Called\n\nEnsure you're subscribing to the event:\n\n```csharp\npdfViewer.LinkTapped += OnLinkTapped;\n```\n\nOr in XAML:\n\n```xml\n\u003cpdf:PdfView LinkTapped=\"OnLinkTapped\" /\u003e\n```\n\n### Android Annotation Events\n\nAnnotation 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.\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n### Third-Party Licenses\n\n- **AhmerPdfium**: Apache License 2.0\n- **PDFKit**: Apple System Framework\n\n## 🙏 Acknowledgments\n\n- **[Ahmer Afzal](https://github.com/AhmerAfzal1)** - AhmerPdfium library maintainer\n- **[barteksc](https://github.com/barteksc)** - Original AndroidPdfViewer\n- **Apple** - PDFKit framework\n- **.NET MAUI Team** - Excellent framework\n\n## 💬 Support\n\n- 🐛 [Issue Tracker](https://github.com/TheEightBot/MauiNativePdfView/issues)\n\n## ⭐ Show Your Support\n\nIf you find this library helpful, please give it a star! ⭐\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**Made with ❤️ for the .NET MAUI community**\n\n[⬆ Back to Top](#-mauinativepdfview)\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheeightbot%2Fmauinativepdfview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftheeightbot%2Fmauinativepdfview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheeightbot%2Fmauinativepdfview/lists"}