pdfrx_fork

Last updated:

0 purchases

pdfrx_fork Image
pdfrx_fork Images
Add to Cart

Description:

pdfrx fork

pdfrx 0.0.1 #

Note
This is a clone version of original flutter package pdfrx which contains some optimized codes supporting for Flutter < 3.19
See original package at: https://pub.dev/packages/pdfrx

pdfrx is a rich and fast PDF viewer implementation built on the top of pdfium.
The plugin supports Android, iOS, Windows, macOS, Linux, and Web.
Interactive Demo #
A demo site using Flutter Web

Main Features #


Zoomable and scrollable PDF document viewer


PDF Link Handling support


Document Outline (a.k.a Bookmarks) support


Text Selection (still experimental) support


Text Search support


Flexibily customizable


Multi-platform support

Android
iOS
Windows
macOS
Linux (even on Raspberry PI)
Web (*using PDF.js)



Three layers of APIs:

Easy to use Flutter widgets

PdfViewer
PdfDocumentViewBuilder
PdfPageView


Easy to use PDF APIs

PdfDocument


pdfium bindings

Not encouraged but you can import package:pdfrx/src/pdfium/pdfium_bindings.dart





Getting Started #
The following fragment illustrates the easiest way to show a PDF file in assets:
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Pdfrx example'),
),
body: PdfViewer.asset('assets/hello.pdf'),
),
);
}
}
copied to clipboard
Installation #
Add this to your package's pubspec.yaml file and execute flutter pub get:
dependencies:
pdfrx: ^0.0.53
copied to clipboard
Windows #


Ensure your Windows installation enables Developer Mode
The build process internally uses symblic link and it requires Developer Mode to be enabled.
Without this, you may encounter errors like this.


Web #
pdf.js is now automatically loaded and no modification to index.html is required.
It's not required but you can customize download URLs for pdf.js by setting PdfJsConfiguration.configuration:
// place the code on main function or somewhere that is executed before the actual
// app code.
PdfJsConfiguration.configuration = const PdfJsConfiguration(
pdfJsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.mjs',
workerSrc: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js',
cMapUrl: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/cmaps/',
);
copied to clipboard
Please note that pdf.js 4.X is not supported yet and use 3.X versions.
macOS #
For macOS, Flutter app restrict its capability by enabling App Sandbox by default. You can change the behavior by editing your app's entitlements files depending on your configuration. See the discussion below.

macos/Runner/Release.entitlements
macos/Runner/DebugProfile.entitlements

Deal with App Sandbox #
The easiest option to access files on your disk, set com.apple.security.app-sandbox to false on your entitlements file though it is not recommended for releasing apps because it completely disables App Sandbox.
Another option is to use com.apple.security.files.user-selected.read-only along with file_selector_macos. The option is better in security than the previous option.
Anyway, the example code for the plugin illustrates how to download and preview internet hosted PDF file. It uses
com.apple.security.network.client along with flutter_cache_manager:
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
copied to clipboard
Open PDF File #
PdfViewer supports following functions to open PDF file on specific medium:

PdfViewer.asset

Open PDF of Flutter's asset


PdfViewer.file

Open PDF from file


PdfViewer.uri

Open PDF from URI (https://... or relative path)
On Flutter Web, it may be blocked by CORS



Deal with Password Protected PDF Files #
To support password protected PDF files, use passwordProvider to supply passwords interactively:
PdfViewer.asset(
'assets/test.pdf',
// Set password provider to show password dialog
passwordProvider: _passwordDialog,

...
),
copied to clipboard
And, _passwordDialog function is defined like this:
Future<String?> _passwordDialog() async {
final textController = TextEditingController();
return await showDialog<String?>(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
title: const Text('Enter password'),
content: TextField(
controller: textController,
autofocus: true,
keyboardType: TextInputType.visiblePassword,
obscureText: true,
onSubmitted: (value) => Navigator.of(context).pop(value),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(null),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(textController.text),
child: const Text('OK'),
),
],
);
},
);
}
copied to clipboard
When PdfViewer tries to open a password protected document, it calls the function passed to passwordProvider (except the first attempt; see below) repeatedly to get a new password until the document is successfully opened. And if the function returns null, the viewer will give up the password trials and the function is no longer called.
firstAttemptByEmptyPassword #
By default, the first password attempt uses empty password. This is because encrypted PDF files frequently use empty password for viewing purpose. It's normally useful but if you want to use authoring password, it can be disabled by setting firstAttemptByEmptyPassword to false.
Customizations #
You can customize the behaviour and visual by configuring PdfViewerParams.
Text Selection #
Text selection feature is still experimental but you can easily enable it like the following fragment:
PdfViewer.asset(
'assets/test.pdf',
enableTextSelection: true,
...
),
copied to clipboard
PDF Link Handling #
To enable Link in PDF file, you should set PdfViewerParams.linkWidgetBuilder.
The following fragment creates a widget that handles user's tap on link:
linkWidgetBuilder: (context, link, size) => Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
// handle URL or Dest
if (link.url != null) {
// TODO: implement your own isSecureUrl by yourself...
if (await isSecureUrl(link.url!)) {
launchUrl(link.url!);
}
} else if (link.dest != null) {
controller.goToDest(link.dest);
}
},
hoverColor: Colors.blue.withOpacity(0.2),
),
),
copied to clipboard
For URIs, you should check the validity of the URIs before opening the URI; the example code just show dialog to ask whether to open the URL or not.
For destinations, you can use PdfViewerController.goToDest to go to the destination. Or you can use PdfViewerController.calcMatrixForDest to get the matrix for it.
Document Outline (a.k.a Bookmarks) #
PDF defines document outline (PdfOutlineNode), which is sometimes called as bookmarks or index. And you can access it by PdfDocument.loadOutline.
The following fragment obtains it on PdfViewerParams.onViewerReady:
onViewerReady: (document, controller) async {
outline.value = await document.loadOutline();
},
copied to clipboard
PdfOutlineNode is tree structured data and for more information, see the usage on example code.
Horizontal Scroll View #
By default, the pages are layed out vertically.
You can customize the layout logic by PdfViewerParams.layoutPages:
layoutPages: (pages, params) {
final height =
pages.fold(0.0, (prev, page) => max(prev, page.height)) +
params.margin * 2;
final pageLayouts = <Rect>[];
double x = params.margin;
for (var page in pages) {
pageLayouts.add(
Rect.fromLTWH(
x,
(height - page.height) / 2, // center vertically
page.width,
page.height,
),
);
x += page.width + params.margin;
}
return PdfPageLayout(
pageLayouts: pageLayouts,
documentSize: Size(x, height),
);
},
copied to clipboard
Facing Pages #
The following code will show pages in "facing-sequential-layout" that is often used in PDF viewer apps:
/// Page reading order; true to L-to-R that is commonly used by books like manga or such
var isRightToLeftReadingOrder = false;
/// Use the first page as cover page
var needCoverPage = true;

...

layoutPages: (pages, params) {
final width = pages.fold(
0.0, (prev, page) => max(prev, page.width));

final pageLayouts = <Rect>[];
final offset = needCoverPage ? 1 : 0;
double y = params.margin;
for (int i = 0; i < pages.length; i++) {
final page = pages[i];
final pos = i + offset;
final isLeft = isRightToLeftReadingOrder
? (pos & 1) == 1
: (pos & 1) == 0;

final otherSide = (pos ^ 1) - offset;
final h = 0 <= otherSide && otherSide < pages.length
? max(page.height, pages[otherSide].height)
: page.height;

pageLayouts.add(
Rect.fromLTWH(
isLeft
? width + params.margin - page.width
: params.margin * 2 + width,
y + (h - page.height) / 2,
page.width,
page.height,
),
);
if (pos & 1 == 1 || i + 1 == pages.length) {
y += h + params.margin;
}
}
return PdfPageLayout(
pageLayouts: pageLayouts,
documentSize: Size(
(params.margin + width) * 2 + params.margin,
y,
),
);
},
copied to clipboard
Showing Scroll Thumbs #
By default, the viewer does never show any scroll bars nor scroll thumbs.
You can add scroll thumbs by using PdfViewerParams.viewerOverlayBuilder:
viewerOverlayBuilder: (context, size) => [
// Add vertical scroll thumb on viewer's right side
PdfViewerScrollThumb(
controller: controller,
orientation: ScrollbarOrientation.right,
thumbSize: const Size(40, 25),
thumbBuilder:
(context, thumbSize, pageNumber, controller) =>
Container(
color: Colors.black,
// Show page number on the thumb
child: Center(
child: Text(
pageNumber.toString(),
style: const TextStyle(color: Colors.white),
),
),
),
),
// Add horizontal scroll thumb on viewer's bottom
PdfViewerScrollThumb(
controller: controller,
orientation: ScrollbarOrientation.bottom,
thumbSize: const Size(80, 30),
thumbBuilder:
(context, thumbSize, pageNumber, controller) =>
Container(
color: Colors.red,
),
),
],
copied to clipboard
Adding Page Number on Page Bottom #
If you want to add page number on each page, you can do that by PdfViewerParams.pageOverlayBuilder:
pageOverlayBuilder: (context, pageRect, page) {
return Align(
alignment: Alignment.bottomCenter,
child: Text(page.pageNumber.toString(),
style: const TextStyle(color: Colors.red)));
},
copied to clipboard
Loading Indicator #
PdfViewer.uri may take long time to download PDF file and you want to show some loading indicator. You can do that by PdfViewerParams.loadingBannerBuilder:
loadingBannerBuilder: (context, bytesDownloaded, totalBytes) {
return Center(
child: CircularProgressIndicator(
// totalBytes may not be available on certain case
value: totalBytes != null ? bytesDownloaded / totalBytes : null,
backgroundColor: Colors.grey,
),
);
}
copied to clipboard
Dark/Night Mode Support #
PdfViewer does not have any native dark (or night) mode support but it can be easily implemented using ColorFiltered widget:
ColorFiltered(
colorFilter: ColorFilter.mode(Colors.white, darkMode ? BlendMode.difference : BlendMode.dst),
child: PdfViewer.file(filePath, ...),
),
copied to clipboard
The trick is originally introduced by pckimlong.
Other Features #
Text Search #
TextSearcher is just a helper class that helps you to implement text searching feature on your app.
The following fragment illustrates the overall structure of the TextSearcher:
class _MainPageState extends State<MainPage> {
final controller = PdfViewerController();
// create a PdfTextSearcher and add a listener to update the GUI on search result changes
late final textSearcher = PdfTextSearcher(controller)..addListener(_update);

void _update() {
if (mounted) {
setState(() {});
}
}

@override
void dispose() {
// dispose the PdfTextSearcher
textSearcher.removeListener(_update);
textSearcher.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Pdfrx example'),
),
body: PdfViewer.asset(
'assets/hello.pdf',
controller: controller,
params: PdfViewerParams(
// add pageTextMatchPaintCallback that paints search hit highlights
pagePaintCallbacks: [
textSearcher.pageTextMatchPaintCallback
],
),
)
);
}
...
}
copied to clipboard
On the fragment above, it does:

Create TextSearcher instance
Add a listener (Using PdfTextSearcher.addListener) to update UI on search result change
Add TextSearcher.pageTextMatchPaintCallback to PdfViewerParams.pagePaintCallbacks to show search matches

Then, you can use TextSearcher.startTextSearch to search text in the PDF document:
textSearcher.startTextSearch('hello', caseInsensitive: true);
copied to clipboard
The search starts running in background and the search progress is notified by the listener.
There are several functions that helps you to navigate user to the search matches:

TextSearcher.goToMatchOfIndex to go to the match of the specified index
TextSearcher.goToNextMatch to go to the next match
TextSearcher.goToPrevMatch to go to the previous match

You can get the search result (even during the search running) in the list of PdfTextRange by PdfTextSearcher.matches:
for (final match in textSearcher.matches) {
print(match.pageNumber);
...
}
copied to clipboard
You can also cancel the background search:
textSearcher.resetTextSearch();
copied to clipboard
PdfDocumentViewBuilder/PdfPageView #
PdfPageView is just another PDF widget that shows only one page. It accepts PdfDocument and page number to show a page within the document.
PdfDocumentViewBuilder is used to safely manage PdfDocument inside widget tree and it accepts builder parameter that creates child widgets.
The following fragment is a typical use of these widgets:
PdfDocumentViewBuilder.asset(
'asset/test.pdf',
builder: (context, document) => ListView.builder(
itemCount: document?.pages.length ?? 0,
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.all(8),
height: 240,
child: Column(
children: [
SizedBox(
height: 220,
child: PdfPageView(
document: document,
pageNumber: index + 1,
alignment: Alignment.center,
),
),
Text(
'${index + 1}',
),
],
),
);
},
),
),
copied to clipboard
PdfDocument Management #
PdfDocumentViewBuilder can accept PdfDocumentRef from PdfViewer to safely share the same PdfDocument instance. For more information, see example/lib/thumbnails_view.dart.

License:

For personal and professional use. You cannot resell or redistribute these repositories in their original state.

Files In This Product:

Customer Reviews

There are no reviews.