Skip to content

Commit c6dc0f2

Browse files
committed
Release 4.3.2
1 parent 563a41e commit c6dc0f2

12 files changed

Lines changed: 208 additions & 56 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 4.3.2 — 30 Apr 2026
2+
3+
- Updated the `getConfiguration` API to return the current `toolbarPosition`. (J#HYB-991)
4+
- Fixes a crash that could occur during `NutrientView` initialization on iOS. (J#HYB-994)
5+
16
## 4.3.1 — 22 Apr 2026
27

38
- Fixes a crash when setting the `toolbarPosition` without the Document Editor component in the Nutrient license. (J#HYB-991)

android/src/main/java/com/pspdfkit/views/PdfView.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,7 @@ public void onViewDetachedFromWindow(@NonNull View view) {}
869869

870870
private void attachPdfFragmentListeners(final PdfUiFragment pdfUiFragment) {
871871
pdfUiFragment.setOnContextualToolbarLifecycleListener(pdfViewModeController);
872+
pdfUiFragment.setOnContextualToolbarMovementListener(pdfViewModeController);
872873
pdfUiFragment.getPSPDFKitViews().getFormEditingBarView().addOnFormEditingBarLifecycleListener(pdfViewModeController);
873874
((ReactPdfUiFragment) pdfUiFragment).setReactPdfUiFragmentListener(new ReactPdfUiFragment.ReactPdfUiFragmentListener() {
874875
@Override
@@ -1346,6 +1347,11 @@ public JSONObject convertConfiguration() {
13461347
config.put("enableFormEditing", fragment.getConfiguration().getConfiguration().isFormEditingEnabled());
13471348
config.put("androidShowAnnotationListAction", fragment.getConfiguration().isAnnotationListEnabled());
13481349

1350+
{
1351+
String pos = getToolbarPosition();
1352+
config.put("toolbarPosition", pos != null ? pos : "top");
1353+
}
1354+
13491355
return config;
13501356
} catch (Exception e) {
13511357
return new JSONObject();

android/src/main/java/com/pspdfkit/views/PdfViewModeController.java

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444
*/
4545
class PdfViewModeController implements
4646
TextSelectionManager.OnTextSelectionModeChangeListener, TextSelectionManager.OnTextSelectionChangeListener,
47-
ToolbarCoordinatorLayout.OnContextualToolbarLifecycleListener, FormEditingBar.OnFormEditingBarLifecycleListener {
47+
ToolbarCoordinatorLayout.OnContextualToolbarLifecycleListener,
48+
ToolbarCoordinatorLayout.OnContextualToolbarMovementListener,
49+
FormEditingBar.OnFormEditingBarLifecycleListener {
4850

4951
private final PdfView parent;
5052

@@ -135,7 +137,9 @@ public void onPrepareContextualToolbar(@NonNull ContextualToolbar contextualTool
135137
// Apply toolbar position configuration
136138
String toolbarPosition = parent.getToolbarPosition();
137139
com.facebook.react.bridge.ReadableArray supportedPositions = parent.getSupportedToolbarPositions();
138-
140+
141+
ToolbarCoordinatorLayout.LayoutParams.Position appliedPosition = null;
142+
139143
if (toolbarPosition != null || supportedPositions != null) {
140144
ToolbarCoordinatorLayout.LayoutParams.Position position = null;
141145
EnumSet<ToolbarCoordinatorLayout.LayoutParams.Position> supportedPositionsSet = null;
@@ -179,13 +183,18 @@ public void onPrepareContextualToolbar(@NonNull ContextualToolbar contextualTool
179183

180184
// Create and set LayoutParams with position and supported positions
181185
contextualToolbar.setLayoutParams(new ToolbarCoordinatorLayout.LayoutParams(finalPosition, supportedPositionsSet));
186+
appliedPosition = finalPosition;
182187
} else if (position != null) {
183188
// Only position is set, use setPosition method
184189
contextualToolbar.setPosition(position);
190+
appliedPosition = position;
185191
}
186192
}
187-
193+
188194
if (contextualToolbar instanceof AnnotationCreationToolbar) {
195+
if (appliedPosition != null) {
196+
parent.setToolbarPosition(toolbarPositionString(appliedPosition));
197+
}
189198
if (itemGroupingRule != null) {
190199
contextualToolbar.setMenuItemGroupingRule(itemGroupingRule);
191200
}
@@ -263,8 +272,29 @@ public void onPrepareContextualToolbar(@NonNull ContextualToolbar contextualTool
263272
}
264273
}
265274

275+
private static String toolbarPositionString(@NonNull ToolbarCoordinatorLayout.LayoutParams.Position position) {
276+
if (position == ToolbarCoordinatorLayout.LayoutParams.Position.TOP) {
277+
return "top";
278+
}
279+
if (position == ToolbarCoordinatorLayout.LayoutParams.Position.LEFT) {
280+
return "left";
281+
}
282+
if (position == ToolbarCoordinatorLayout.LayoutParams.Position.RIGHT) {
283+
return "right";
284+
}
285+
return "top";
286+
}
287+
288+
private void syncToolbarPositionFromContextualToolbar(@NonNull ContextualToolbar contextualToolbar) {
289+
ToolbarCoordinatorLayout.LayoutParams.Position position = contextualToolbar.getPosition();
290+
if (position != null) {
291+
parent.setToolbarPosition(toolbarPositionString(position));
292+
}
293+
}
294+
266295
@Override
267296
public void onDisplayContextualToolbar(@NonNull ContextualToolbar contextualToolbar) {
297+
syncToolbarPositionFromContextualToolbar(contextualToolbar);
268298
if (contextualToolbar instanceof AnnotationCreationToolbar) {
269299
annotationCreationActive = true;
270300
}
@@ -277,6 +307,7 @@ public void onDisplayContextualToolbar(@NonNull ContextualToolbar contextualTool
277307

278308
@Override
279309
public void onRemoveContextualToolbar(@NonNull ContextualToolbar contextualToolbar) {
310+
syncToolbarPositionFromContextualToolbar(contextualToolbar);
280311
if (contextualToolbar instanceof AnnotationCreationToolbar) {
281312
annotationCreationActive = false;
282313
}
@@ -287,6 +318,21 @@ public void onRemoveContextualToolbar(@NonNull ContextualToolbar contextualToolb
287318
parent.updateState();
288319
}
289320

321+
@Override
322+
public void onDragContextualToolbar(@NonNull ContextualToolbar contextualToolbar, int dx, int dy) {
323+
syncToolbarPositionFromContextualToolbar(contextualToolbar);
324+
}
325+
326+
@Override
327+
public void onAttachContextualToolbar(@NonNull ContextualToolbar contextualToolbar) {
328+
syncToolbarPositionFromContextualToolbar(contextualToolbar);
329+
}
330+
331+
@Override
332+
public void onDetachContextualToolbar(@NonNull ContextualToolbar contextualToolbar) {
333+
syncToolbarPositionFromContextualToolbar(contextualToolbar);
334+
}
335+
290336
@Override
291337
public void onPrepareFormEditingBar(@NonNull FormEditingBar formEditingBar) {
292338
// Not required.

ios/Common/NutrientPropsDocumentHelper.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
1616
+ (void)applyDocumentFromJSON:(id)json
1717
remoteDocumentConfig:(nullable NSDictionary *)remoteDocumentConfig
1818
toView:(RCTPSPDFKitView *)view
19-
usingManager:(PDFDocumentManager *)manager
19+
usingManager:(nullable PDFDocumentManager *)manager
2020
withReference:(NSNumber *)identifier;
2121

2222
+ (void)applyPageIndexFromJSON:(id)json

ios/Common/NutrientPropsDocumentHelper.m

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,25 @@
2323

2424
@implementation NutrientPropsDocumentHelper
2525

26-
+ (void)applyDocumentFromJSON:(id)json remoteDocumentConfig:(NSDictionary *)remoteDocumentConfig toView:(RCTPSPDFKitView *)view usingManager:(PDFDocumentManager *)manager withReference:(NSNumber *)identifier {
26+
+ (void)applyDocumentFromJSON:(id)json remoteDocumentConfig:(NSDictionary * _Nullable)remoteDocumentConfig toView:(RCTPSPDFKitView *)view usingManager:(PDFDocumentManager * _Nullable)manager withReference:(NSNumber *)identifier {
2727
if (!json) { return; }
2828
view.pdfController.document = [RCTConvert PSPDFDocument:json
2929
remoteDocumentConfig:remoteDocumentConfig];
3030
view.pdfController.document.delegate = (id<PSPDFDocumentDelegate>)view;
3131

3232
#if RCT_NEW_ARCH_ENABLED
33-
[manager setDocument:view.pdfController.document reference:identifier];
33+
[PDFDocumentStore setDocument:view.pdfController.document reference:identifier];
34+
[PDFDocumentStore setView:view forReference:identifier];
35+
[PDFDocumentStore setDelegate:(id<PDFDocumentManagerDelegate>)view forReference:identifier];
3436
#else
37+
NSCParameterAssert(manager != nil);
38+
if (manager == nil) {
39+
return;
40+
}
3541
[manager setDocument:view.pdfController.document reference:view.reactTag];
42+
[manager setView:view forReference:view.reactTag];
43+
[manager setDelegate:(id<PDFDocumentManagerDelegate>)view];
3644
#endif
37-
[manager setDelegate:(id<PDFDocumentManagerDelegate>)view];
3845

3946
if (view.annotationAuthorName) {
4047
view.pdfController.document.defaultAnnotationUsername = view.annotationAuthorName;

ios/Converters/RCTConvert+PSPDFConfiguration.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,19 @@
126126
#define BookmarkSortOrderMap @{@"custom" : @(PSPDFBookmarkManagerSortOrderCustom), \
127127
@"pageBased" : @(PSPDFBookmarkManagerSortOrderPageBased)} \
128128

129+
static NSString *StringForPSPDFFlexibleToolbarPosition(PSPDFFlexibleToolbarPosition position) {
130+
if (position == PSPDFFlexibleToolbarPositionTop) {
131+
return @"top";
132+
}
133+
if (position == PSPDFFlexibleToolbarPositionLeft) {
134+
return @"left";
135+
}
136+
if (position == PSPDFFlexibleToolbarPositionRight) {
137+
return @"right";
138+
}
139+
return @"top";
140+
}
141+
129142
@implementation RCTConvert (PSPDFConfiguration)
130143

131144
+ (PSPDFConfiguration *)PSPDFConfiguration:(id)json {
@@ -556,6 +569,15 @@ + (NSDictionary *)convertConfiguration:(PSPDFViewController *)viewController {
556569

557570
[convertedConfiguration setObject:[RCTConvert findKeyForValue:viewController.appearanceModeManager.appearanceMode
558571
inDictionary:AppearanceModeMap] forKey:@"appearanceMode"];
572+
573+
NSString *toolbarPosition = @"top";
574+
if ([PSPDFKitGlobal isFeatureEnabled:PSPDFFeatureMaskAnnotationEditing]) {
575+
PSPDFFlexibleToolbar *annotationToolbar = viewController.annotationToolbarController.annotationToolbar;
576+
if (annotationToolbar != nil) {
577+
toolbarPosition = StringForPSPDFFlexibleToolbarPosition(annotationToolbar.toolbarPosition);
578+
}
579+
}
580+
[convertedConfiguration setObject:toolbarPosition forKey:@"toolbarPosition"];
559581

560582
return convertedConfiguration;
561583
}

ios/Fabric/NutrientView.mm

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434
#import <React/RCTViewComponentView.h>
3535
#import <React/UIView+React.h>
3636

37-
#import <React/RCTBridge.h>
38-
3937
// Codegen headers (generated by RN based on package.json codegenConfig)
4038
#import <react/renderer/components/nutrient_sdk_react_native_codegen/ComponentDescriptors.h>
4139
#import <react/renderer/components/nutrient_sdk_react_native_codegen/Props.h>
@@ -44,11 +42,6 @@
4442

4543
using namespace facebook::react;
4644

47-
// Currently there is no way to access the Turbo / Native modules, aside from using the bridge
48-
@interface RCTBridge (Private)
49-
+ (RCTBridge *)currentBridge;
50-
@end
51-
5245
@interface NutrientView () <RCTNutrientViewViewProtocol, RCTComponentViewProtocol, RCTPSPDFKitViewDelegate>
5346
@end
5447

@@ -106,6 +99,7 @@ - (instancetype)initWithFrame:(CGRect)frame
10699
[_view.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
107100
[_view.trailingAnchor constraintEqualToAnchor:self.trailingAnchor]
108101
]];
102+
109103
}
110104
return self;
111105
}
@@ -336,11 +330,13 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
336330
// Basic document props
337331
if (!newProps->document.empty()) {
338332
_document = RCTNSStringFromString(newProps->document);
339-
PDFDocumentManager *documentManager = [[RCTBridge currentBridge] moduleForClass:[PDFDocumentManager class]];
340333
NSNumber *reference = [NSNumber numberWithInteger:[self.nativeId integerValue]];
341334
NSDictionary *remoteConfig = jsonConfig[@"remoteDocumentConfiguration"];
342-
[NutrientPropsDocumentHelper applyDocumentFromJSON:_document remoteDocumentConfig:remoteConfig toView:_view usingManager:documentManager withReference:reference];
343-
[documentManager setView:_view forReference:reference];
335+
[NutrientPropsDocumentHelper applyDocumentFromJSON:_document
336+
remoteDocumentConfig:remoteConfig
337+
toView:_view
338+
usingManager:nil
339+
withReference:reference];
344340
[NutrientPropsFontHelper configureCustomFontPickerForView:_view];
345341
}
346342

@@ -445,6 +441,7 @@ - (void)dealloc
445441
// Unregister this view from the registry when deallocated
446442
if (self.nativeId) {
447443
[[NutrientViewRegistry shared] unregisterViewWithId:self.nativeId];
444+
[PDFDocumentStore removeReference:[NSNumber numberWithInteger:[self.nativeId integerValue]]];
448445
}
449446

450447
// Clean up delegate reference
@@ -468,6 +465,7 @@ - (void)prepareForRecycle
468465
// Unregister this view from the registry when recycled
469466
if (self.nativeId) {
470467
[[NutrientViewRegistry shared] unregisterViewWithId:self.nativeId];
468+
[PDFDocumentStore removeReference:[NSNumber numberWithInteger:[self.nativeId integerValue]]];
471469
}
472470

473471
// Reset the view state to ensure clean reuse

ios/PDFDocumentManager.swift

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,50 +11,29 @@ import Foundation
1111
import React
1212
import PSPDFKit
1313

14-
@objc public protocol PDFDocumentManagerDelegate {
14+
@objc public protocol PDFDocumentManagerDelegate: AnyObject {
1515
@objc optional func didReceiveAnnotationChange(change: String, annotations: Array<Annotation>)
1616
@objc optional func reloadControllerData()
1717
}
1818

19-
private class WeakViewRef {
20-
weak var view: RCTPSPDFKitView?
21-
init(_ view: RCTPSPDFKitView) { self.view = view }
22-
}
23-
2419
@objc(PDFDocumentManager) public class PDFDocumentManager: NSObject {
25-
26-
var documents = [NSNumber:Document]()
20+
2721
@objc public var delegate: PDFDocumentManagerDelegate?
28-
private var viewByReference = [NSNumber: WeakViewRef]()
29-
private let queue = DispatchQueue(label: "io.nutrient.reactnative.documentmanager")
30-
22+
3123
private func getDocument(_ reference: NSNumber) -> Document? {
32-
var result: Document?
33-
queue.sync {
34-
result = documents[reference]
35-
}
36-
return result
24+
return PDFDocumentStore.getDocument(reference)
3725
}
38-
26+
3927
private func getView(for reference: NSNumber) -> RCTPSPDFKitView? {
40-
var result: RCTPSPDFKitView?
41-
queue.sync {
42-
result = viewByReference[reference]?.view
43-
}
44-
return result
28+
return PDFDocumentStore.getView(for: reference)
4529
}
46-
30+
4731
@objc public func setDocument(_ document: Document, reference: NSNumber) {
48-
queue.async {
49-
self.documents[reference] = document
50-
}
32+
PDFDocumentStore.setDocument(document, reference: reference)
5133
}
52-
34+
5335
@objc public func setView(_ view: Any, forReference reference: NSNumber) {
54-
guard let pdfView = view as? RCTPSPDFKitView else { return }
55-
queue.async {
56-
self.viewByReference[reference] = WeakViewRef(pdfView)
57-
}
36+
PDFDocumentStore.setView(view, forReference: reference)
5837
}
5938

6039
@objc static public func requiresMainQueueSetup() -> Bool {
@@ -137,7 +116,8 @@ private class WeakViewRef {
137116
DispatchQueue.main.async {
138117
documentProvider.setRotationOffset(rotationValue, forPageAt: PageIndex(pageIndex))
139118
SDK.shared.cache.invalidateImages(from: document, pageIndex: PageIndex(pageIndex))
140-
self.delegate?.reloadControllerData?()
119+
let callbackDelegate = PDFDocumentStore.getDelegate(for: reference) ?? self.delegate
120+
callbackDelegate?.reloadControllerData?()
141121
onSuccess(true)
142122
}
143123
}
@@ -432,11 +412,12 @@ private class WeakViewRef {
432412
}
433413
}
434414

435-
// Delegate to RCTPSPDFKitView to reload controller data
436-
delegate?.reloadControllerData?()
415+
// Delegate to RCTPSPDFKitView to reload controller data.
416+
let callbackDelegate = PDFDocumentStore.getDelegate(for: reference) ?? self.delegate
417+
callbackDelegate?.reloadControllerData?()
437418
onSuccess(true)
438419
// Emit the onAnnotationsChanged event since document.applyInstantJSON doesn't trigger the event on iOS
439-
delegate?.didReceiveAnnotationChange?(change: "added", annotations: annotationArray)
420+
callbackDelegate?.didReceiveAnnotationChange?(change: "added", annotations: annotationArray)
440421
return
441422
}
442423
catch {

0 commit comments

Comments
 (0)