Skip to content

Add support to render pdf page using pdfium with the help of optional package - syncfusion_pdfviewer_android #2368

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/syncfusion_flutter_pdfviewer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
## Unreleased

**Bugs**

* Now, in single page layout mode, the page changes only when swiped in the scroll direction, and not when slightly panned.
* Now, the `SfPdfViewer` widget will no longer crash with an Out of Memory exception when viewing a PDF document with large page dimensions.
* Now, in single-page layout mode, setting the zoom level in the onPageChanged callback will not cause the page to move to the bottom in the vertical scroll direction or to the right in the horizontal scroll direction.
* Now, the sticky note icon will maintain the same size on all pages, and the icon size is improved on mobile platforms.

## [29.1.41] - 05/06/2025

**Bugs**

* Improved the performance in loading large password-protected documents by leveraging the password parameter supported in the native (platform) PDF rendering APIs.
* Now, the pages will be centered in single-page layout mode when switching between pages in a PDF document with different page sizes.

## [29.1.39] - 04/22/2025

**General**

* The minimum Dart version has been updated to 3.7.

**Bugs**

* Now, the page no longer jumps to a previous one when scrolling to the top or left edge in single page layout mode.

## [29.1.33] - 03/25/2025

**General**

* The compatible version of our Flutter PDF Viewer widget has been updated to Flutter SDK 3.29.0.
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,18 @@ public class SyncfusionFlutterPdfViewerPlugin: NSObject, FlutterPlugin {
guard let args = argument as? [String: Any] else {return}
guard let documentBytes = args["documentBytes"] as? FlutterStandardTypedData else {return}
guard let documentID = args["documentID"] as? String else {return}
let password = args["password"] as? String
let byte = [UInt8](documentBytes.data)
guard let cfData = CFDataCreate(nil, byte, byte.count) else {return}
guard let dataProvider = CGDataProvider(data: cfData) else {return}
guard let document = CGPDFDocument(dataProvider) else {return}
if let password = password, document.isEncrypted {
let unlocked = document.unlockWithPassword(password)
if !unlocked {
result(FlutterError(code: "PDF_UNLOCK_FAILED", message: "Failed to unlock the PDF with the provided password", details: nil))
return
}
}
self.documentRepo[documentID] = document
let pageCount = NSNumber(value: document.numberOfPages)
result(pageCount.stringValue);
Expand Down Expand Up @@ -228,4 +236,4 @@ public class SyncfusionFlutterPdfViewerPlugin: NSObject, FlutterPlugin {
return nil
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ import 'annotation_view.dart';
import 'text_markup.dart';

/// Callback definition for annotation property change.
typedef AnnotationPropertyChangedCallback = void Function(
Annotation annotation,
String propertyName,
Object oldValue,
Object newValue,
);
typedef AnnotationPropertyChangedCallback =
void Function(
Annotation annotation,
String propertyName,
Object oldValue,
Object newValue,
);

/// Callback definition for annotation property change.
typedef AnnotationPropertyChangingCallback = bool Function(
Annotation annotation, String propertyName);
typedef AnnotationPropertyChangingCallback =
bool Function(Annotation annotation, String propertyName);

/// Represents a PDF annotation.
abstract class Annotation extends ChangeNotifier {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ class _AnnotationContainerState extends State<AnnotationContainer> {
List<Annotation> annotations = <Annotation>[];
if (_selectedAnnotation != null &&
_selectedAnnotation!.pageNumber == widget.pageNumber) {
_updateAnnotationGlobalRect(_selectedAnnotation!);
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateAnnotationGlobalRect(_selectedAnnotation!);
});
for (final Annotation annotation in widget.annotations) {
if (annotation != _selectedAnnotation) {
annotations.add(annotation);
Expand Down Expand Up @@ -123,10 +125,8 @@ class _AnnotationContainerState extends State<AnnotationContainer> {
return Positioned(
left: annotation.uiBounds.left / widget.heightPercentage,
top: annotation.uiBounds.top / widget.heightPercentage,
width: (annotation.uiBounds.width / widget.zoomLevel) /
widget.heightPercentage,
height: (annotation.uiBounds.height / widget.zoomLevel) /
widget.heightPercentage,
width: annotation.uiBounds.width / widget.zoomLevel,
height: annotation.uiBounds.height / widget.zoomLevel,
child: Visibility(
visible: !widget.isZooming,
child: _getAnnotationView(annotation),
Expand Down Expand Up @@ -196,13 +196,12 @@ class _AnnotationContainerState extends State<AnnotationContainer> {
key: ValueKey<Annotation>(annotation),
annotation: annotation,
isSelected: annotation == _selectedAnnotation,
heightPercentage: widget.heightPercentage * widget.zoomLevel,
zoomLevel: widget.zoomLevel,
canEdit: !isLocked,
selectorColor: isLocked
? widget.annotationSettings.selector.lockedColor
: widget.annotationSettings.selector.color,
selectorStorkeWidth: selectionBorderThickness /
(widget.heightPercentage * widget.zoomLevel),
selectorStorkeWidth: selectionBorderThickness / widget.zoomLevel,
onAnnotationMoved: annotation.isSelected ? onAnnotationMoved : null,
onAnnotationMoving: annotation.isSelected ? onAnnotationMoving : null,
onTap: () {
Expand Down Expand Up @@ -256,18 +255,22 @@ class _AnnotationContainerState extends State<AnnotationContainer> {
if (newPosition.dy < 0) {
newPosition = Offset(newPosition.dx, 0);
}
if (newPosition.dx + annotation.intermediateBounds.width >
if (newPosition.dx +
(annotation.intermediateBounds.width * widget.heightPercentage) >
widget.pageSize.width) {
newPosition = Offset(
widget.pageSize.width - annotation.intermediateBounds.width,
widget.pageSize.width -
(annotation.intermediateBounds.width * widget.heightPercentage),
newPosition.dy,
);
}
if (newPosition.dy + annotation.intermediateBounds.height >
if (newPosition.dy +
(annotation.intermediateBounds.height * widget.heightPercentage) >
widget.pageSize.height) {
newPosition = Offset(
newPosition.dx,
widget.pageSize.height - annotation.intermediateBounds.height,
widget.pageSize.height -
(annotation.intermediateBounds.height * widget.heightPercentage),
);
}
if (annotation is StickyNoteAnnotation) {
Expand All @@ -277,14 +280,20 @@ class _AnnotationContainerState extends State<AnnotationContainer> {

void _updateAnnotationGlobalRect(Annotation annotation) {
if (annotation is StickyNoteAnnotation) {
if (!mounted) {
return;
}
final renderObject = context.findRenderObject();
if (renderObject is RenderBox) {
final Rect scaledRect =
annotation.uiBounds.topLeft &
annotation.uiBounds.size * widget.heightPercentage;
annotation.globalRect = Rect.fromPoints(
renderObject.localToGlobal(
annotation.uiBounds.topLeft / widget.heightPercentage,
scaledRect.topLeft / widget.heightPercentage,
),
renderObject.localToGlobal(
annotation.uiBounds.bottomRight / widget.heightPercentage,
scaledRect.bottomRight / widget.heightPercentage,
),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,22 @@ class StickyNoteAnnotationView extends InteractiveGraphicsView
bool canEdit = true,
Color selectorColor = defaultSelectorColor,
double selectorStorkeWidth = 1,
double heightPercentage = 1,
double zoomLevel = 1,
}) : super(
key: key,
color: annotation.color,
strokeWidth: 1,
opacity: annotation.opacity,
isSelected: isSelected,
selectorColor: selectorColor,
canMove: canEdit,
selectorStorkeWidth: selectorStorkeWidth,
) {
_heightPercentage = heightPercentage;
key: key,
color: annotation.color,
strokeWidth: 1,
opacity: annotation.opacity,
isSelected: isSelected,
selectorColor: selectorColor,
canMove: canEdit,
selectorStorkeWidth: selectorStorkeWidth,
) {
_zoomLevel = zoomLevel;
}

/// Height percentage of the pdf page.
late final double _heightPercentage;
/// Zoom level of the pdf page.
late final double _zoomLevel;

/// Called when the annotation is moved.
final AnnotationMoveEndedCallback? onAnnotationMoved;
Expand All @@ -171,7 +171,7 @@ class StickyNoteAnnotationView extends InteractiveGraphicsView
isSelected: isSelected,
selectorColor: selectorColor,
selectorStorkeWidth: selectorStorkeWidth,
heightPercentage: _heightPercentage,
zoomLevel: _zoomLevel,
onAnnotationMoved: onAnnotationMoved,
onAnnotationMoving: onAnnotationMoving,
onDoubleTap: onDoubleTap,
Expand All @@ -186,7 +186,7 @@ class StickyNoteAnnotationView extends InteractiveGraphicsView
) {
if (renderObject is RenderStickyNoteAnnotationView) {
renderObject
..heightPercentage = _heightPercentage
..zoomLevel = _zoomLevel
..selectorStorkeWidth = selectorStorkeWidth
..onAnnotationMoved = onAnnotationMoved
..onAnnotationMoving = onAnnotationMoving
Expand Down Expand Up @@ -214,18 +214,18 @@ class RenderStickyNoteAnnotationView extends RenderInteractiveGraphicsView {
this.onAnnotationMoving,
VoidCallback? onTap,
void Function()? onDoubleTap,
double heightPercentage = 1,
}) : _onDoubleTap = onDoubleTap,
super(
strokeColor: color,
opacity: opacity,
strokeWidth: 1,
isSelected: isSelected,
selectorColor: selectorColor,
selectorStorkeWidth: selectorStorkeWidth,
) {
double zoomLevel = 1,
}) : _onDoubleTap = onDoubleTap,
super(
strokeColor: color,
opacity: opacity,
strokeWidth: 1,
isSelected: isSelected,
selectorColor: selectorColor,
selectorStorkeWidth: selectorStorkeWidth,
) {
_onTap = onTap;
_heightPercentage = heightPercentage;
_zoomLevel = zoomLevel;
_selectorStorkeWidth = selectorStorkeWidth;

_doubleTapGestureRecognizer = DoubleTapGestureRecognizer()
Expand All @@ -238,19 +238,19 @@ class RenderStickyNoteAnnotationView extends RenderInteractiveGraphicsView {
_fillPath = Path();
}

late double _heightPercentage;
late double _zoomLevel;
late DoubleTapGestureRecognizer _doubleTapGestureRecognizer;
late Path _fillPath;
late Path _strokePath;
late double _selectorStorkeWidth;

/// The height percentage.
double get heightPercentage => _heightPercentage;
set heightPercentage(double value) {
if (_heightPercentage == value) {
/// The zoom level
double get zoomLevel => _zoomLevel;
set zoomLevel(double value) {
if (_zoomLevel == value) {
return;
}
_heightPercentage = value;
_zoomLevel = value;
markNeedsPaint();
}

Expand Down Expand Up @@ -297,10 +297,10 @@ class RenderStickyNoteAnnotationView extends RenderInteractiveGraphicsView {
Rect _getPaintRect(Rect rect, Offset offset) {
final Rect localRect = rect.translate(-_bounds.left, -_bounds.top);
final Offset globalOffset = Offset(
offset.dx + localRect.left / heightPercentage,
offset.dy + localRect.top / heightPercentage,
offset.dx + (localRect.left / zoomLevel),
offset.dy + (localRect.top / zoomLevel),
);
return globalOffset & (localRect.size / heightPercentage);
return globalOffset & (localRect.size / zoomLevel);
}

void _applyRotationTransform(Canvas canvas, int rotation, Offset offset) {
Expand Down
Loading