Skip to content

Commit 5c78994

Browse files
authored
Merge pull request #48 from PSPDFKit/simone/37-instant-document-json
Add Instant document JSON support
2 parents 70697d5 + dec5583 commit 5c78994

File tree

10 files changed

+240
-95
lines changed

10 files changed

+240
-95
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ adb push /path/to/your/document.pdf /sdcard/document.pdf
216216
url: git://github.com/PSPDFKit/pspdfkit-flutter.git
217217
```
218218

219-
4. Add a `PDFs` directory with a document in it in the root directory: `myapp/PDFs/Guide_v4.pdf` and specify it in your `pubspec.yaml`:
219+
4. Add a `PDFs` directory with a document in it in the root directory: `myapp/PDFs/PSPDFKit.pdf` and specify it in your `pubspec.yaml`:
220220

221221
```yaml
222222
assets:
@@ -264,7 +264,7 @@ import 'package:flutter/services.dart';
264264
import 'package:path_provider/path_provider.dart';
265265
import 'package:pspdfkit_flutter/pspdfkit.dart';
266266
267-
const String DOCUMENT_PATH = 'PDFs/Guide_v4.pdf';
267+
const String DOCUMENT_PATH = 'PDFs/PSPDFKit.pdf';
268268
const String PSPDFKIT_FLUTTER_PLUGIN_TITLE = 'PSPDFKit Flutter Plugin example app';
269269
const String OPEN_DOCUMENT_BUTTON = 'Tap to Open Document';
270270
const String PSPDFKIT_FOR = 'PSPDFKit for';

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

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,29 @@
1818
import android.util.Log;
1919

2020
import androidx.annotation.NonNull;
21+
import androidx.annotation.Nullable;
2122
import androidx.core.app.ActivityCompat;
2223
import androidx.core.content.ContextCompat;
2324

2425
import com.pspdfkit.PSPDFKit;
26+
import com.pspdfkit.document.PdfDocument;
27+
import com.pspdfkit.document.formatters.DocumentJsonFormatter;
28+
import com.pspdfkit.document.providers.InputStreamDataProvider;
2529
import com.pspdfkit.forms.ChoiceFormElement;
2630
import com.pspdfkit.forms.EditableButtonFormElement;
2731
import com.pspdfkit.forms.SignatureFormElement;
2832
import com.pspdfkit.forms.TextFormElement;
2933
import com.pspdfkit.ui.PdfActivityIntentBuilder;
3034

35+
import java.io.ByteArrayInputStream;
36+
import java.io.ByteArrayOutputStream;
37+
import java.io.InputStream;
38+
import java.nio.charset.StandardCharsets;
3139
import java.util.ArrayList;
3240
import java.util.HashMap;
3341
import java.util.Iterator;
3442
import java.util.List;
43+
import java.util.UUID;
3544
import java.util.concurrent.atomic.AtomicReference;
3645

3746
import io.flutter.plugin.common.MethodCall;
@@ -43,6 +52,7 @@
4352
import io.reactivex.android.schedulers.AndroidSchedulers;
4453
import io.reactivex.schedulers.Schedulers;
4554

55+
import static com.pspdfkit.flutter.pspdfkit.util.Preconditions.requireDocumentNotNull;
4656
import static com.pspdfkit.flutter.pspdfkit.util.Preconditions.requireNotNullNotEmpty;
4757

4858
/**
@@ -76,6 +86,7 @@ public static void registerWith(Registrar registrar) {
7686
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
7787
String fullyQualifiedName;
7888
FlutterPdfActivity flutterPdfActivity;
89+
PdfDocument document;
7990

8091
switch (call.method) {
8192
case "frameworkVersion":
@@ -128,20 +139,47 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
128139
openSettings();
129140
result.success(true);
130141
break;
142+
case "applyInstantJson":
143+
String annotationsJson = call.argument("annotationsJson");
144+
145+
requireNotNullNotEmpty(annotationsJson, "annotationsJson");
146+
document = requireDocumentNotNull(FlutterPdfActivity.getCurrentActivity(), "Pspdfkit.applyInstantJson(String)");
147+
148+
DocumentJsonDataProvider documentJsonDataProvider = new DocumentJsonDataProvider(annotationsJson);
149+
//noinspection ResultOfMethodCallIgnored
150+
DocumentJsonFormatter.importDocumentJsonAsync(document, documentJsonDataProvider)
151+
.subscribeOn(Schedulers.io())
152+
.observeOn(AndroidSchedulers.mainThread())
153+
.subscribe(
154+
() -> result.success(true),
155+
throwable -> result.error(LOG_TAG,
156+
"Error while importing document Instant JSON",
157+
throwable.getMessage()));
158+
break;
159+
case "exportInstantJson":
160+
document = requireDocumentNotNull(FlutterPdfActivity.getCurrentActivity(), "Pspdfkit.exportInstantJson()");
161+
162+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
163+
//noinspection ResultOfMethodCallIgnored
164+
DocumentJsonFormatter.exportDocumentJsonAsync(document, outputStream)
165+
.subscribeOn(Schedulers.io())
166+
.observeOn(AndroidSchedulers.mainThread())
167+
.subscribe(
168+
() -> result.success(outputStream.toString(StandardCharsets.UTF_8.name())),
169+
throwable -> result.error(LOG_TAG,
170+
"Error while exporting document Instant JSON",
171+
throwable.getMessage()));
172+
break;
131173
case "setFormFieldValue":
132174
String value = call.argument("value");
133175
fullyQualifiedName = call.argument("fullyQualifiedName");
134176

135177
requireNotNullNotEmpty(value, "Value");
136178
requireNotNullNotEmpty(fullyQualifiedName, "Fully qualified name");
137-
flutterPdfActivity = FlutterPdfActivity.getCurrentActivity();
138-
if (flutterPdfActivity == null) {
139-
throw new IllegalStateException("Before using \"Pspdfkit.setFormFieldValue()\" " +
140-
"the document needs to be presented by calling \"Pspdfkit.present()\".");
141-
}
179+
document = requireDocumentNotNull(FlutterPdfActivity.getCurrentActivity(), "Pspdfkit.setFormFieldValue(String)");
142180

143-
//noinspection ResultOfMethodCallIgnored,ConstantConditions
144-
flutterPdfActivity.getPdfFragment().getDocument().getFormProvider()
181+
//noinspection ResultOfMethodCallIgnored
182+
document.getFormProvider()
145183
.getFormElementWithNameAsync(fullyQualifiedName)
146184
.subscribeOn(Schedulers.computation())
147185
.observeOn(AndroidSchedulers.mainThread())
@@ -187,14 +225,10 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
187225
fullyQualifiedName = call.argument("fullyQualifiedName");
188226

189227
requireNotNullNotEmpty(fullyQualifiedName, "Fully qualified name");
190-
flutterPdfActivity = FlutterPdfActivity.getCurrentActivity();
191-
if (flutterPdfActivity == null) {
192-
throw new IllegalStateException("Before using \"Pspdfkit.setFormFieldValue()\" " +
193-
"the document needs to be presented by calling \"Pspdfkit.present()\".");
194-
}
228+
document = requireDocumentNotNull(FlutterPdfActivity.getCurrentActivity(), "Pspdfkit.getFormFieldValue()");
195229

196-
//noinspection ResultOfMethodCallIgnored,ConstantConditions
197-
flutterPdfActivity.getPdfFragment().getDocument().getFormProvider()
230+
//noinspection ResultOfMethodCallIgnored
231+
document.getFormProvider()
198232
.getFormElementWithNameAsync(fullyQualifiedName)
199233
.subscribeOn(Schedulers.computation())
200234
.observeOn(AndroidSchedulers.mainThread())
@@ -233,13 +267,10 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
233267
);
234268
break;
235269
case "save":
236-
flutterPdfActivity = FlutterPdfActivity.getCurrentActivity();
237-
if (flutterPdfActivity == null) {
238-
throw new IllegalStateException("Before using \"Pspdfkit.save()\" " +
239-
"the document needs to be presented by calling \"Pspdfkit.present()\".");
240-
}
241-
//noinspection ResultOfMethodCallIgnored,ConstantConditions
242-
flutterPdfActivity.getPdfFragment().getDocument().saveIfModifiedAsync()
270+
document = requireDocumentNotNull(FlutterPdfActivity.getCurrentActivity(), "Pspdfkit.save()");
271+
272+
//noinspection ResultOfMethodCallIgnored
273+
document.saveIfModifiedAsync()
243274
.subscribeOn(Schedulers.computation())
244275
.observeOn(AndroidSchedulers.mainThread())
245276
.subscribe(result::success);
@@ -353,4 +384,37 @@ public boolean onRequestPermissionsResult(int requestCode, String[] permissions,
353384
}
354385
return status == 2;
355386
}
387+
388+
/** A small in-memory data provider for loading the Document instant JSON from a string. */
389+
private static class DocumentJsonDataProvider extends InputStreamDataProvider {
390+
@NonNull private final byte[] annotationsJsonBytes;
391+
@NonNull private final UUID uuid;
392+
393+
DocumentJsonDataProvider(@NonNull final String annotationsJson) {
394+
this.annotationsJsonBytes = annotationsJson.getBytes(StandardCharsets.UTF_8);
395+
this.uuid = UUID.nameUUIDFromBytes(annotationsJsonBytes);
396+
}
397+
@NonNull
398+
@Override
399+
protected InputStream openInputStream() {
400+
return new ByteArrayInputStream(annotationsJsonBytes);
401+
}
402+
403+
@Override
404+
public long getSize() {
405+
return annotationsJsonBytes.length;
406+
}
407+
408+
@NonNull
409+
@Override
410+
public String getUid() {
411+
return String.format("document-instant-json-%s", uuid.toString());
412+
}
413+
414+
@Nullable
415+
@Override
416+
public String getTitle() {
417+
return null;
418+
}
419+
}
356420
}

android/src/main/java/com/pspdfkit/flutter/pspdfkit/util/Preconditions.java

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,57 @@
88
*/
99
package com.pspdfkit.flutter.pspdfkit.util;
1010

11+
import androidx.annotation.NonNull;
1112
import androidx.annotation.Nullable;
1213

14+
import com.pspdfkit.document.PdfDocument;
15+
import com.pspdfkit.flutter.pspdfkit.FlutterPdfActivity;
16+
1317
public class Preconditions {
1418
/**
1519
* Checks that the given <code>string</code> is not null and not empty,
1620
* or throws an {@link IllegalArgumentException} with the provided <code>readableName</code>.
21+
*
1722
* @return Returns the original <code>string</code>.
1823
*/
1924
public static String requireNotNullNotEmpty(@Nullable String string, @Nullable String readableName) {
20-
if (string == null) throw new IllegalArgumentException(String.format("%s may not be null.", readableName));
21-
if (string.isEmpty()) throw new IllegalArgumentException(String.format("%s may not be empty.", readableName));
25+
if (string == null)
26+
throw new IllegalArgumentException(String.format("%s may not be null.", readableName));
27+
if (string.isEmpty())
28+
throw new IllegalArgumentException(String.format("%s may not be empty.", readableName));
2229
return string;
2330
}
31+
32+
/**
33+
* Checks if the given assertion is satisfied (evaluates to true) and if not,
34+
* throws {@link IllegalStateException} with the provided message.
35+
*
36+
* @param stateAssertion Assertion to check against.
37+
* @param exceptionMessage Message of the {@link IllegalStateException}, in case it needs to be thrown.
38+
*/
39+
public static void requireState(boolean stateAssertion, @Nullable String exceptionMessage) {
40+
if (!stateAssertion) {
41+
throw new IllegalStateException(exceptionMessage);
42+
}
43+
}
44+
45+
/**
46+
* Checks that the document has been correctly presented and that the static instance of
47+
* the Flutter Activity is not null.
48+
*
49+
* @param flutterPdfActivity Flutter Activity to check that may not be null.
50+
* @param invokedMethod Invoked method to use in the error message in case of an illegal state.
51+
* @return Returns the loaded document.
52+
*/
53+
@NonNull
54+
public static PdfDocument requireDocumentNotNull(@Nullable FlutterPdfActivity flutterPdfActivity, @NonNull String invokedMethod) {
55+
if (flutterPdfActivity == null) {
56+
throw new IllegalStateException(String.format("Before using \"%s\" " +
57+
"the document needs to be presented by calling \"Pspdfkit.present()\".", invokedMethod));
58+
}
59+
requireState(flutterPdfActivity.getPdfFragment() != null, "PdfFragment may not be null.");
60+
requireState(flutterPdfActivity.getPdfFragment().getDocument() != null, "PdfDocument may not be null.");
61+
62+
return flutterPdfActivity.getPdfFragment().getDocument();
63+
}
2464
}

example/PDFs/Guide_v4.pdf

-2.12 MB
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"annotations": [{"bbox": [18.4609375, 582.3333740234375, 431.6640625, 426.00003051757812], "blendMode": "normal", "createdAt": "2019-11-15T15:36:25Z", "creatorName": "simone", "isDrawnNaturally": false, "lineWidth": 10, "lines": {"intensities": [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], "points": [[[271.6171875, 598], [266.93331909179688, 595.16668701171875], [259.71206665039062, 591.5833740234375], [255.27398681640625, 590.16668701171875], [248.65992736816406, 588.75], [244.59376525878906, 588.0030517578125], [239.75138854980469, 587.3333740234375], [234.60507202148438, 587.3333740234375], [229.98934936523438, 587.3333740234375], [225.3984375, 588.5908203125], [218.64601135253906, 588.75], [212.66595458984375, 588.75], [206.78047180175781, 590.16668701171875], [190.50941467285156, 591.5833740234375], [179.1796875, 593.75], [167.80718994140625, 595.16668701171875], [158.66848754882812, 596.5833740234375], [150.61857604980469, 598], [144.00840759277344, 600.125], [139.43266296386719, 600.125], [135.82032775878906, 601.54168701171875], [130.93865966796875, 604.41668701171875], [125.97789764404297, 607.9583740234375], [120.16405487060547, 609.6793212890625], [114.53083038330078, 612.2083740234375], [109.08804321289062, 614.375], [104.01295471191406, 616.29022216796875], [98.109375, 619.43707275390625], [92.337196350097656, 622.16668701171875], [87.625740051269531, 625.04168701171875], [82.543983459472656, 628.5833740234375], [77.507820129394531, 631.41668701171875], [71.68023681640625, 636.41668701171875], [68.25, 641.36651611328125], [63.281246185302734, 644.66424560546875], [58.289066314697266, 650.6976318359375], [54.0234375, 658.99774169921875], [49.0546875, 669.7379150390625], [42.656242370605469, 681.2083740234375], [37.6875, 694.83612060546875], [32.695316314697266, 707.46807861328125], [29.859375, 717.4583740234375], [28.429689407348633, 730.241943359375], [24.867185592651367, 742.06866455078125], [24.867185592651367, 753.7083740234375], [23.4609375, 768.94134521484375], [23.4609375, 782.875], [23.4609375, 799.98004150390625], [23.4609375, 814.16668701171875], [24.867185592651367, 828.375], [27, 841.2083740234375], [29.859375, 854], [32.695316314697266, 867.92138671875], [36.257816314697266, 880.78863525390625], [41.226566314697266, 893.8333740234375], [42.656242370605469, 904.97161865234375], [44.085941314697266, 913.21966552734375], [45.185382843017578, 919.41668701171875], [48.017143249511719, 928.0272216796875], [51.890621185302734, 937.18316650390625], [56.882816314697266, 946.41668701171875], [61.938720703125, 952.8333740234375], [64.687507629394531, 957.05609130859375], [70.581169128417969, 962.0833740234375], [78.143966674804688, 968.4583740234375], [86.742179870605469, 973.4583740234375], [96.703125, 979.875], [109.5, 984.125], [124.87102508544922, 989.0833740234375], [137.95753479003906, 992.66668701171875], [149.32032775878906, 995.5], [163.54685974121094, 999.04168701171875], [184.17189025878906, 1000.4583740234375], [209.76560974121094, 1001.9166870117188], [231.79685974121094, 1003.3333740234375], [250.99217224121094, 1003.3333740234375], [265.053955078125, 1003.3333740234375], [276.60940551757812, 1003.3333740234375], [287.2734375, 1001.9166870117188], [301.83041381835938, 1000.2388916015625], [317.83590698242188, 995.5], [333.4921875, 992.66668701171875], [345.98687744140625, 984.57415771484375], [355.52838134765625, 976.28839111328125], [366.91409301757812, 965.625], [384.67971801757812, 952.8333740234375], [401.74224853515625, 941.4583740234375], [414.5625, 932.2083740234375], [425.454345703125, 924.84600830078125], [432.32809448242188, 915.875], [436.59375, 901.6387939453125], [441.58590698242188, 881], [442.99221801757812, 854], [445.125, 830.54168701171875], [445.125, 807.79168701171875], [442.99221801757812, 788.7923583984375], [440.15625, 771.5], [435.16409301757812, 752.2916259765625], [430.19534301757812, 736.66668701171875], [427.35934448242188, 724.387451171875], [424.5, 715.0751953125], [420.96090698242188, 708.2083740234375], [415.96810913085938, 698.25], [410.59469604492188, 690.4583740234375], [406.734375, 682.9306640625], [403.17190551757812, 677.268798828125], [400.33590698242188, 671.87744140625], [395.66424560546875, 668.41668701171875], [389.95599365234375, 662.67138671875], [381.23092651367188, 657], [373.3125, 650.625], [366.91409301757812, 645.625], [360.19537353515625, 641.375], [354.17913818359375, 637.8333740234375], [349.00265502929688, 634.9583740234375], [345.44509887695312, 631.41668701171875], [339.6290283203125, 628.5833740234375], [334.68048095703125, 625.04168701171875], [330.84097290039062, 623.5833740234375], [325.87319946289062, 622.16668701171875], [319.62060546875, 620.75], [315.2069091796875, 618.625], [309.99786376953125, 618.6029052734375], [304.51095581054688, 617.2083740234375], [300.08163452148438, 617.2083740234375], [297.23440551757812, 616.544189453125], [292.9212646484375, 615.7916259765625], [287.88607788085938, 615.7916259765625], [283.19888305664062, 615.7916259765625], [277.85714721679688, 615.7916259765625], [273.72171020507812, 615.043701171875], [268.078125, 614.58349609375], [264.75930786132812, 615.7916259765625], [260.88003540039062, 615.7916259765625], [256.7109375, 615.7916259765625], [252.39967346191406, 617.2083740234375], [247.453125, 622.875], [243.87890625, 623.5833740234375], [239.98826599121094, 625.04168701171875]]]}, "name": "ed9b4f2f-8178-4b69-9c50-bb59f820e10e", "opacity": 1, "pageIndex": 0, "strokeColor": "#2196F3", "type": "pspdfkit/ink", "updatedAt": "2019-11-15T15:36:25Z", "v": 1}], "format": "https://pspdfkit.com/instant-json/v1", "pdfId": {"changing": "3llO8Q1c9h7lim6xrsd3BQ==", "permanent": "2ShIBg/xQBK0uIs748b8Ng=="}}

example/PDFs/PSPDFKit.pdf

2.95 MB
Binary file not shown.

0 commit comments

Comments
 (0)