Skip to content

Commit 2b9d430

Browse files
committed
Release 5.6.0
1 parent 9f183cf commit 2b9d430

35 files changed

Lines changed: 941 additions & 380 deletions

ACKNOWLEDGEMENTS.md

Lines changed: 4 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,7 @@ THE SOFTWARE.
987987
## Expat (https://libexpat.github.io/)
988988

989989
Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
990-
Copyright (c) 2001-2017 Expat maintainers
990+
Copyright (c) 2001-2026 Expat maintainers
991991

992992
Permission is hereby granted, free of charge, to any person obtaining
993993
a copy of this software and associated documentation files (the
@@ -2315,16 +2315,16 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
23152315
PNG Reference Library License version 2
23162316
---------------------------------------
23172317

2318-
* Copyright (c) 1995-2018 The PNG Reference Library Authors.
2319-
* Copyright (c) 2018 Cosmin Truta.
2318+
* Copyright (c) 1995-2026 The PNG Reference Library Authors.
2319+
* Copyright (c) 2018-2026 Cosmin Truta.
23202320
* Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
23212321
* Copyright (c) 1996-1997 Andreas Dilger.
23222322
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
23232323

23242324
The software is supplied "as is", without warranty of any kind,
23252325
express or implied, including, without limitation, the warranties
23262326
of merchantability, fitness for a particular purpose, title, and
2327-
non-infringement. In no even shall the Copyright owners, or
2327+
non-infringement. In no event shall the Copyright owners, or
23282328
anyone distributing the software, be liable for any damages or
23292329
other liability, whether in contract, tort or otherwise, arising
23302330
from, out of, or in connection with the software, or the use or
@@ -4192,23 +4192,6 @@ This Source Code Form is subject to the terms of the Mozilla Public
41924192
License, v. 2.0. If a copy of the MPL was not distributed with this
41934193
file, You can obtain one at http://mozilla.org/MPL/2.0/
41944194

4195-
--------------------------------------------------------------------------------
4196-
## ReLinker (https://github.com/KeepSafe/ReLinker)
4197-
4198-
Copyright 2015 - 2016 Keepsafe Software Inc.
4199-
4200-
Licensed under the Apache License, Version 2.0 (the "License");
4201-
you may not use this file except in compliance with the License.
4202-
You may obtain a copy of the License at
4203-
4204-
http://www.apache.org/licenses/LICENSE-2.0
4205-
4206-
Unless required by applicable law or agreed to in writing, software
4207-
distributed under the License is distributed on an "AS IS" BASIS,
4208-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4209-
See the License for the specific language governing permissions and
4210-
limitations under the License.
4211-
42124195
--------------------------------------------------------------------------------
42134196
## RxAndroid (https://github.com/ReactiveX/RxAndroid)
42144197

@@ -4741,32 +4724,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
47414724
SOFTWARE.
47424725

47434726

4744-
--------------------------------------------------------------------------------
4745-
## swift-perception (https://github.com/pointfreeco/swift-perception)
4746-
4747-
MIT License
4748-
4749-
Copyright (c) 2023 Point-Free
4750-
4751-
Permission is hereby granted, free of charge, to any person obtaining a copy
4752-
of this software and associated documentation files (the "Software"), to deal
4753-
in the Software without restriction, including without limitation the rights
4754-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4755-
copies of the Software, and to permit persons to whom the Software is
4756-
furnished to do so, subject to the following conditions:
4757-
4758-
The above copyright notice and this permission notice shall be included in all
4759-
copies or substantial portions of the Software.
4760-
4761-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4762-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4763-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4764-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4765-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4766-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
4767-
SOFTWARE.
4768-
4769-
47704727
--------------------------------------------------------------------------------
47714728
## Tesseract (https://tesseract-ocr.github.io/)
47724729

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## 5.6.0 — 22 Jun 2026
2+
3+
- Adds AI Assistant support to `NutrientInstantView` on Android and iOS via the new `aiAssistantConfiguration` field on `NutrientViewConfiguration`. (#53210)
4+
- Adds `enableInstantComments` to `NutrientViewConfiguration` to enable Instant Comments on `NutrientInstantView` on Android and iOS. (#53271)
5+
- Updates the minimum iOS deployment target to iOS 17. (J#IOS-1103)
6+
- Updates Nutrient iOS SDK to 26.7.0. (#54611)
7+
- Updates Nutrient Android SDK to 11.5.1. (#54079)
8+
- Fixes an Android issue where AI Assistant citations didn’t navigate to the referenced page. (#53210)
9+
- Fixes an Android crash that occurred when opening AI Assistant before the document finished loading. (#53210)
10+
- Fixes an issue where calling `Nutrient.presentInstant(...)` with a `PdfConfiguration` caused a crash. (#53271)
11+
- Fixes an Android crash that occurred when opening a `NutrientView` with `startPage` set. (#54611)
12+
- Fixes an iOS issue where the “Add Comment” item was missing from Instant document menus when `enableInstantComments` was set. (#53271)
13+
- Fixes an iOS issue where the signature editor could get stuck after adding an image to a signature from the camera. (J#HYB-1001)
14+
115
## 5.5.1 - 16 Apr 2026
216

317
- Fixes `Nutrient.present(...)` failing on iOS apps using the `UIScene` lifecycle. (J#HYB-992)

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Add powerful PDF functionality to your Flutter apps with the Nutrient Flutter SDK. View, annotate, and edit PDFs seamlessly across Android, iOS, and Web platforms.
44

5+
![Nutrient Flutter SDK](screenshots/flutter.png)
6+
57
## Requirements
68

79
- Flutter SDK (latest stable version)
@@ -11,7 +13,7 @@ Add powerful PDF functionality to your Flutter apps with the Nutrient Flutter SD
1113
- Android Virtual Device or physical device
1214
- For iOS:
1315
- Xcode 16 or later
14-
- iOS 16.0 or later
16+
- iOS 17.0 or later
1517
- For Web:
1618
- Modern web browser with WebAssembly support
1719

@@ -77,10 +79,10 @@ class MainActivity: FlutterAppCompatActivity() {
7779

7880
### iOS Setup
7981

80-
Make sure to set the minimum iOS version to 16.0 in your `ios/Podfile`:
82+
Make sure to set the minimum iOS version to 17.0 in your `ios/Podfile`:
8183

8284
```ruby
83-
platform :ios, '16.0'
85+
platform :ios, '17.0'
8486
```
8587

8688
### Web Setup

android/config.gradle

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ if (nutrientMavenUrl == null || nutrientMavenUrl == '') {
3838

3939
ext.nutrientVersion = localProperties.getProperty('pspdfkit.version')
4040
if (nutrientVersion == null || nutrientVersion == '') {
41-
ext.nutrientVersion = '11.2.1'
41+
// Pinned to the released Nutrient Android SDK for this release. The plugin uses
42+
// 11.5 APIs (AnnotationToolbar / AnnotationPopupToolbar /
43+
// OnPreparePopupToolbarListener). Override via pspdfkit.version in
44+
// local.properties to build against a nightly/SNAPSHOT.
45+
ext.nutrientVersion = '11.5.1'
4246
}
4347

4448
ext.nutrientMavenModuleName = 'nutrient'

android/src/main/java/com/pspdfkit/flutter/pspdfkit/ConfigurationAdapter.java

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,14 @@ class ConfigurationAdapter {
276276
if (key != null) {
277277
configureUserInterfaceViewMode((String) configurationMap.get(key));
278278
}
279-
key = getKeyOfType(configurationMap, START_PAGE, Long.class);
280-
if (key != null) {
281-
configureStartPage(((Long) configurationMap.get(key)).intValue());
279+
// Flutter's StandardMessageCodec encodes small ints as Integer and large
280+
// ones as Long, so accept any Number rather than strictly Long.
281+
Object startPageValue = configurationMap.get(START_PAGE);
282+
if (startPageValue == null) {
283+
startPageValue = configurationMap.get(addAndroidPrefix(START_PAGE));
284+
}
285+
if (startPageValue instanceof Number) {
286+
configureStartPage(((Number) startPageValue).intValue());
282287
}
283288
key = getKeyOfType(configurationMap, SHOW_SEARCH_ACTION, Boolean.class);
284289
if (key != null) {
@@ -1058,27 +1063,27 @@ private <T> String getKeyOfType(@NonNull HashMap<String, Object> configurationMa
10581063
private static <T> String javaToDartTypeConverted(Class<T> clazz) {
10591064
if (clazz == null) {
10601065
return "null";
1061-
} else if (clazz.isInstance(Boolean.class)) {
1066+
} else if (Boolean.class.isAssignableFrom(clazz)) {
10621067
return "bool";
1063-
} else if (clazz.isInstance(Integer.class)) {
1068+
} else if (Integer.class.isAssignableFrom(clazz)) {
10641069
return "int";
1065-
} else if (clazz.isInstance(Long.class)) {
1070+
} else if (Long.class.isAssignableFrom(clazz)) {
10661071
return "int";
1067-
} else if (clazz.isInstance(Double.class)) {
1072+
} else if (Double.class.isAssignableFrom(clazz)) {
10681073
return "double";
1069-
} else if (clazz.isInstance(String.class)) {
1074+
} else if (String.class.isAssignableFrom(clazz)) {
10701075
return "String";
1071-
} else if (clazz.isInstance(byte[].class)) {
1076+
} else if (byte[].class.isAssignableFrom(clazz)) {
10721077
return "Uint8List";
1073-
} else if (clazz.isInstance(int[].class)) {
1078+
} else if (int[].class.isAssignableFrom(clazz)) {
10741079
return "Int32List";
1075-
} else if (clazz.isInstance(long[].class)) {
1080+
} else if (long[].class.isAssignableFrom(clazz)) {
10761081
return "Int64List";
1077-
} else if (clazz.isInstance(double[].class)) {
1082+
} else if (double[].class.isAssignableFrom(clazz)) {
10781083
return "Float64List";
1079-
} else if (clazz.isInstance(ArrayList.class)) {
1084+
} else if (ArrayList.class.isAssignableFrom(clazz)) {
10801085
return "List";
1081-
} else if (clazz.isInstance(HashMap.class)) {
1086+
} else if (HashMap.class.isAssignableFrom(clazz)) {
10821087
return "Map";
10831088
}
10841089
throw new IllegalArgumentException("Undefined dart type conversion for " + clazz.getName());

android/src/main/java/com/pspdfkit/flutter/pspdfkit/FlutterInstantPdfActivity.kt

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,47 @@
99

1010
package com.pspdfkit.flutter.pspdfkit
1111

12+
import android.graphics.RectF
1213
import android.os.Bundle
14+
import android.util.Log
1315
import androidx.fragment.app.Fragment
16+
import com.pspdfkit.ai.createAiAssistantForInstant
1417
import com.pspdfkit.document.PdfDocument
18+
import com.pspdfkit.flutter.pspdfkit.ai.FlutterAiAssistantRegistry
1519
import com.pspdfkit.flutter.pspdfkit.util.MeasurementHelper
1620
import com.pspdfkit.instant.document.InstantPdfDocument
1721
import com.pspdfkit.instant.exceptions.InstantException
1822
import com.pspdfkit.instant.ui.InstantPdfActivity
1923
import io.flutter.plugin.common.MethodChannel
24+
import io.nutrient.domain.ai.AiAssistant
25+
import io.nutrient.domain.ai.AiAssistantProvider
2026
import java.util.concurrent.atomic.AtomicReference
2127

28+
// Activities don't have a meaningful "view id" the way platform views do. We
29+
// pick a sentinel that won't collide with real Flutter platform view ids
30+
// (those start at 0 and grow positive).
31+
private const val INSTANT_ACTIVITY_VIEW_ID = -1
32+
2233
/**
2334
* For communication with the PSPDFKit plugin, we keep a static reference to the current
2435
* activity.
2536
*/
26-
class FlutterInstantPdfActivity : InstantPdfActivity() {
37+
class FlutterInstantPdfActivity : InstantPdfActivity(), AiAssistantProvider {
38+
39+
override fun getAiAssistant(): AiAssistant? =
40+
FlutterAiAssistantRegistry.get(INSTANT_ACTIVITY_VIEW_ID)
41+
?: FlutterAiAssistantRegistry.getActive()
42+
43+
override fun navigateTo(documentRect: List<RectF>, pageIndex: Int, documentIndex: Int) {
44+
try {
45+
pdfFragment?.let { fragment ->
46+
fragment.setPageIndex(pageIndex, true)
47+
fragment.highlight(this, documentRect, pageIndex)
48+
}
49+
} catch (t: Throwable) {
50+
Log.e(LOG_TAG, "AI Assistant navigateTo failed", t)
51+
}
52+
}
2753

2854

2955
override fun onCreate(savedInstanceState: Bundle?) {
@@ -40,6 +66,10 @@ class FlutterInstantPdfActivity : InstantPdfActivity() {
4066
override fun onDestroy() {
4167
super.onDestroy()
4268
releaseActivity()
69+
// Release the AI Assistant socket so it doesn't outlive the activity.
70+
FlutterAiAssistantRegistry.unregister(INSTANT_ACTIVITY_VIEW_ID)
71+
// No static state to clear — config now lives in this activity's
72+
// Intent extras and is garbage-collected with the activity instance.
4373
}
4474

4575
override fun onDocumentLoaded(pdfDocument: PdfDocument) {
@@ -51,6 +81,40 @@ class FlutterInstantPdfActivity : InstantPdfActivity() {
5181
MeasurementHelper.addMeasurementConfiguration(fragment, it)
5282
}
5383
}
84+
setupAiAssistantForInstant(pdfDocument)
85+
}
86+
87+
private fun setupAiAssistantForInstant(pdfDocument: PdfDocument) {
88+
val aiServerUrl = intent.getStringExtra(EXTRA_AI_SERVER_URL) ?: return
89+
val aiJwt = intent.getStringExtra(EXTRA_AI_JWT) ?: return
90+
val sessionId = intent.getStringExtra(EXTRA_AI_SESSION_ID) ?: return
91+
val instantServerUrl = intent.getStringExtra(EXTRA_INSTANT_SERVER_URL) ?: run {
92+
Log.e(LOG_TAG, "AI Assistant: missing Instant server URL in Intent extras")
93+
return
94+
}
95+
val instantDoc = pdfDocument as? InstantPdfDocument ?: run {
96+
Log.w(LOG_TAG, "AI Assistant: expected InstantPdfDocument, got ${pdfDocument::class.java.simpleName}")
97+
return
98+
}
99+
val layerJwt = instantDoc.instantDocumentDescriptor.jwt
100+
if (layerJwt == null) {
101+
Log.e(LOG_TAG, "AI Assistant: missing Instant layer JWT")
102+
return
103+
}
104+
try {
105+
val assistant = createAiAssistantForInstant(
106+
context = this,
107+
instantServerUrl = instantServerUrl,
108+
documentLayerJwts = listOf(layerJwt),
109+
aiAssistantServerUrl = aiServerUrl,
110+
sessionId = sessionId,
111+
jwtToken = { _ -> aiJwt }
112+
)
113+
FlutterAiAssistantRegistry.register(INSTANT_ACTIVITY_VIEW_ID, assistant)
114+
Log.d(LOG_TAG, "AI Assistant (Instant) created for document ${instantDoc.uid}")
115+
} catch (e: Exception) {
116+
Log.e(LOG_TAG, "Error creating AI Assistant for Instant document", e)
117+
}
54118
}
55119

56120
override fun onDocumentLoadFailed(throwable: Throwable) {
@@ -116,8 +180,18 @@ class FlutterInstantPdfActivity : InstantPdfActivity() {
116180
}
117181

118182
companion object {
183+
private const val LOG_TAG = "PSPDFKitPlugin"
119184
private var measurementValueConfigurations:List<Map<String,Any>>? = null
120185

186+
// Intent extras: each FlutterInstantPdfActivity instance owns its own
187+
// AI Assistant configuration via these keys, so a rapid sequence of
188+
// presentInstant() calls can no longer race on a shared static slot.
189+
// See PR #53248 review comment on the static-config race.
190+
const val EXTRA_AI_SERVER_URL = "com.pspdfkit.flutter.AI_SERVER_URL"
191+
const val EXTRA_AI_JWT = "com.pspdfkit.flutter.AI_JWT"
192+
const val EXTRA_AI_SESSION_ID = "com.pspdfkit.flutter.AI_SESSION_ID"
193+
const val EXTRA_INSTANT_SERVER_URL = "com.pspdfkit.flutter.INSTANT_SERVER_URL"
194+
121195
@JvmStatic
122196
var currentActivity: FlutterInstantPdfActivity? = null
123197
private set

android/src/main/java/com/pspdfkit/flutter/pspdfkit/FlutterPdfActivity.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,20 @@ public static void setMeasurementValueConfigurations(@Nullable final List<Map<St
4444
}
4545

4646
@Override
47-
protected void onCreate(Bundle bundle) {
47+
public void onCreate(Bundle bundle) {
4848
super.onCreate(bundle);
4949
bindActivity();
5050
}
5151

5252
@Override
53-
protected void onPause() {
53+
public void onPause() {
5454
// Notify the Flutter PSPDFKit plugin that the activity is going to enter the onPause state.
5555
EventDispatcher.getInstance().notifyActivityOnPause();
5656
super.onPause();
5757
}
5858

5959
@Override
60-
protected void onDestroy() {
60+
public void onDestroy() {
6161
super.onDestroy();
6262
releaseActivity();
6363
}

0 commit comments

Comments
 (0)