Skip to content

Commit

Permalink
API Support for setting stamp image programmatically given URL (#627)
Browse files Browse the repository at this point in the history
* iOS implementation - hygen generated methods

iOS portion of configuration - ran into blocker where sample used to set custom image for annot doesn't seem to work to expectation

android sample partially implemented

* ios implementation fix

uncomment network call but commenting the local call

* Android implementation

added new .kt file to async download file and provide callback to documentview to update stamp image

* format

random blank space

* update iOS implementation

sample changes fixed image update on iOS

* Updating package version

* syntax missing

missing }

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
darrenchann and github-actions[bot] authored Dec 8, 2022
1 parent a61c0db commit f1c6880
Show file tree
Hide file tree
Showing 15 changed files with 366 additions and 4 deletions.
11 changes: 11 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@

buildscript {
ext.kotlinVersion = '1.6.0'
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:4.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}

Expand All @@ -21,6 +23,9 @@ rootProject.allprojects {
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 31
Expand All @@ -37,6 +42,9 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
lintOptions {
abortOnError false
}
Expand All @@ -57,6 +65,9 @@ dependencies {

implementation 'androidx.fragment:fragment:1.2.1'

implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'

implementation "com.pdftron:pdftron:9.4.1"
implementation "com.pdftron:tools:9.4.1"
implementation "com.pdftron:collab:9.4.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,22 @@ public void run() {

// Hygen Generated Methods

@ReactMethod
public void setStampImageData(final int tag, final String annotationId, final int pageNumber, final String stampImageDataUrl, final Promise promise) {
getReactApplicationContext().runOnUiQueueThread(new Runnable() {
@Override
public void run() {
try {
mDocumentViewInstance.setStampImageData(tag, annotationId, pageNumber, stampImageDataUrl, promise);
promise.resolve(null);
} catch (Exception ex) {
promise.reject(ex);
}
}
});
}


@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
mDocumentViewInstance.onActivityResult(requestCode, resultCode, data);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.pdftron.reactnative.utils

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileOutputStream
import java.net.URL

interface DownloadFileCallback {
fun downloadSuccess(path : String)

fun downloadFailed(e : Exception)
}

fun downloadFromURL(link : String, path: String, callback : DownloadFileCallback) {
CoroutineScope(Job() + Dispatchers.IO).launch {
try {
URL(link).openStream().use { input ->
FileOutputStream(File(path)).use { output ->
input.copyTo(output)
callback.downloadSuccess(path)
}
}

} catch (e : Exception) {
e.printStackTrace()
callback.downloadFailed(e)
}
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
Expand Down Expand Up @@ -1427,6 +1428,14 @@ public String getSavedSignatureJpgFolder(int tag) throws PDFNetException {
}

// Hygen Generated Methods
public void setStampImageData(int tag, String annotationId, int pageNumber, String stampImageDataUrl, Promise promise) throws PDFNetException {
DocumentView documentView = mDocumentViews.get(tag);
if (documentView != null) {
documentView.setStampImageData(annotationId, pageNumber, stampImageDataUrl, promise);
} else {
throw new PDFNetException("", 0L, getName(), "setStampImageData", "Unable to find DocumentView.");
}
}

@Override
public boolean needsCustomLayoutForChildren() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import androidx.fragment.app.FragmentManager;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
Expand All @@ -43,7 +44,11 @@
import com.pdftron.pdf.Annot;
import com.pdftron.pdf.ColorPt;
import com.pdftron.pdf.DigitalSignatureField;
import com.pdftron.pdf.Element;
import com.pdftron.pdf.ElementBuilder;
import com.pdftron.pdf.ElementWriter;
import com.pdftron.pdf.Field;
import com.pdftron.pdf.Image;
import com.pdftron.pdf.PDFDoc;
import com.pdftron.pdf.PDFViewCtrl;
import com.pdftron.pdf.Page;
Expand Down Expand Up @@ -95,6 +100,8 @@
import com.pdftron.reactnative.nativeviews.RNCollabViewerTabHostFragment;
import com.pdftron.reactnative.nativeviews.RNPdfViewCtrlTabFragment;
import com.pdftron.reactnative.nativeviews.RNPdfViewCtrlTabHostFragment;
import com.pdftron.reactnative.utils.DocumentViewUtilsKt;
import com.pdftron.reactnative.utils.DownloadFileCallback;
import com.pdftron.reactnative.utils.ReactUtils;
import com.pdftron.sdf.Obj;

Expand Down Expand Up @@ -4873,6 +4880,61 @@ public String getSavedSignatureJpgFolder() {
}

// Hygen Generated Methods
public void setStampImageData(String annotationId, int pageNumber, String stampImageDataUrl, Promise promise) throws PDFNetException {
// Initialize a new ElementWriter and ElementBuilder
ElementWriter writer = new ElementWriter();
ElementBuilder builder = new ElementBuilder();

writer.begin(getPdfViewCtrl().getDoc().getSDFDoc(), true);

Annot annot = ViewerUtils.getAnnotById(getPdfViewCtrl().getDoc(), annotationId, pageNumber);
File file = new File(getContext().getFilesDir(), "image.png");
DocumentViewUtilsKt.downloadFromURL(stampImageDataUrl, file.getAbsolutePath(), new DownloadFileCallback() {
@Override
public void downloadSuccess(@NonNull String path) {
// Initialize the new image
int w, h = 0;
try {
Image image = Image.create(getPdfViewCtrl().getDoc().getSDFDoc(), path);

w = image.getImageWidth();
h = image.getImageHeight();
// Initialize a new image element
Element element = builder.createImage(image, 0, 0, w, h);

// Write the element
writer.writePlacedElement(element);

// Get the bounding box of the new element
com.pdftron.pdf.Rect bbox = element.getBBox();

// Configure the appearance stream that will be written to the annotation
Obj new_appearance_stream = writer.end();

// Set the bounding box to be the rect of the new element
new_appearance_stream.putRect(
"BBox",
bbox.getX1(),
bbox.getY1(),
bbox.getX2(),
bbox.getY2());

// Overwrite the annotation's appearance with the new appearance stream
annot.setAppearance(new_appearance_stream);

getPdfViewCtrl().update(annot, pageNumber);
} catch (PDFNetException e) {
e.printStackTrace();
}
promise.resolve(annotationId);
}

@Override
public void downloadFailed(@NonNull Exception e) {
promise.reject("setStampData Error", e);
}
});
}

public void setSaveStateEnabled(boolean saveStateEnabled) {
mSaveStateEnabled = saveStateEnabled;
Expand Down
14 changes: 12 additions & 2 deletions example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ export default class App extends Component<Props> {
onLeadingNavButtonPressed = () => {
console.log('leading nav button pressed');
if (this._viewer) {
this._viewer.exportAnnotations().then((xfdf) => {
console.log('xfdf', xfdf);
this._viewer.setStampImageData().then((annotationId, pageNumber, stampImageDataUrl) => {
annotationID = '75911d3a-f1fa-7a4f-8137-5885e3a4c4ae',
pageNumber = 1,
stampImageData = 'https://media.sproutsocial.com/uploads/2017/02/10x-featured-social-media-image-size.png';
});
}

Expand Down Expand Up @@ -66,6 +68,12 @@ export default class App extends Component<Props> {
console.log('xfdfCommand', xfdfCommand);
}

setStampImageData = ({annotationId, pageNumber, stampImageDataUrl}) => {
annotationID = '75911d3a-f1fa-7a4f-8137-5885e3a4c4ae',
pageNumber = 1,
stampImageData = 'https://media.sproutsocial.com/uploads/2017/02/10x-featured-social-media-image-size.png';
}

render() {
const path = "https://pdftron.s3.amazonaws.com/downloads/pl/PDFTRON_about.pdf";
const myToolbar = {
Expand Down Expand Up @@ -97,6 +105,8 @@ export default class App extends Component<Props> {
disabledTools={[Config.Tools.annotationCreateLine, Config.Tools.annotationCreateRectangle]}
fitMode={Config.FitMode.FitPage}
layoutMode={Config.LayoutMode.Continuous}
setStampImageData = {this.setStampImageData}
openOutlineList = {true}
/>
);
}
Expand Down
2 changes: 2 additions & 0 deletions ios/RNTPTDocumentView.h
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,8 @@ static NSString * const PTSignaturesManager_signatureDirectory = @"PTSignaturesM
-(NSString *)getSavedSignatureFolder;

// Hygen Generated Methods
- (void)setStampImageData:(NSString *)annotationId pageNumber:(NSInteger)pageNumber stampImageDataUrl:(NSString *)stampImageDataUrl;

@end


Expand Down
61 changes: 61 additions & 0 deletions ios/RNTPTDocumentView.m
Original file line number Diff line number Diff line change
Expand Up @@ -6146,6 +6146,67 @@ - (void)openThumbnailsView

#pragma mark - Hygen Generated Props/Methods

- (void)setStampImageData:(NSString *)annotationId pageNumber:(NSInteger)pageNumber stampImageDataUrl:(NSString *)stampImageDataUrl
{
NSURL *imageUrl = [NSURL URLWithString: stampImageDataUrl];

NSURLSessionDataTask* task = [NSURLSession.sharedSession dataTaskWithURL:imageUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
return;
}

// Initialize the new image with downloaded file
PTObjSet* hintSet = [[PTObjSet alloc] init];
PTObj* encoderHints = [hintSet CreateArray];

NSString *compressionAlgorithm = @"png";
NSInteger compressionQuality = 50;
[encoderHints PushBackName:compressionAlgorithm];
[encoderHints PushBackName:@"Quality"];
[encoderHints PushBackNumber:compressionQuality];
PTPDFDoc* doc = [self.currentDocumentViewController.pdfViewCtrl GetDoc];
PTImage* image = [PTImage CreateWithDataSimple:[doc GetSDFDoc] buf:data buf_size:data.length encoder_hints:encoderHints];

PTAnnot *annot = [self findAnnotWithUniqueID:annotationId
onPageNumber:(int)pageNumber
pdfViewCtrl:self.currentDocumentViewController.pdfViewCtrl];
[self setCustomImage:image OnAnnotation:annot onDoc:doc];
[self.currentDocumentViewController.pdfViewCtrl UpdateWithAnnot:annot page_num:(int)pageNumber];
}];

[task resume];

}

- (void)setCustomImage:(PTImage*)image OnAnnotation:(PTAnnot*)annot onDoc:(PTPDFDoc*)doc
{
// Initialize a new PTElementWriter and PTElementBuilder
PTElementWriter* writer = [[PTElementWriter alloc] init];
PTElementBuilder* builder = [[PTElementBuilder alloc] init];

[writer WriterBeginWithSDFDoc:[doc GetSDFDoc] compress:YES];

int w = [image GetImageWidth], h = [image GetImageHeight];

// Initialize a new image element
PTElement* img_element = [builder CreateImageWithCornerAndScale:image x:0 y:0 hscale:w vscale:h];

// Write the element
[writer WritePlacedElement:img_element];

// Get the bounding box of the new element
PTPDFRect* bbox = [img_element GetBBox];

// Configure the appearance stream that will be written to the annotation
PTObj* appearance_stream = [writer End];

// Set the bounding box to be the rect of the new element
[appearance_stream PutRect:@"BBox" x1:[bbox GetX1] y1:[bbox GetY1] x2:[bbox GetX2] y2:[bbox GetY2]];

// Overwrite the annotation's appearance with the new appearance stream
[annot SetAppearance:appearance_stream annot_state:e_ptnormal app_state:0];
}

- (void)setForceAppTheme:(NSString *)forcedAppTheme
{
_forceAppTheme = forcedAppTheme;
Expand Down
1 change: 1 addition & 0 deletions ios/RNTPTDocumentViewManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,6 @@
- (NSString *)getSavedSignatureFolderForDocumentViewTag:(NSNumber *)tag;

#pragma mark - Hygen Generated Methods
- (void)setStampImageDataForDocumentViewTag:(NSNumber *)tag annotationId:(NSString *)annotationId pageNumber:(NSInteger)pageNumber stampImageDataUrl:(NSString *)stampImageDataUrl;

@end
10 changes: 10 additions & 0 deletions ios/RNTPTDocumentViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -1692,6 +1692,16 @@ - (NSString *)getSavedSignatureFolderForDocumentViewTag:(NSNumber *)tag
}

#pragma mark - Hygen Generated Methods
- (void)setStampImageDataForDocumentViewTag:(NSNumber *)tag annotationId:(NSString *)annotationId pageNumber:(NSInteger)pageNumber stampImageDataUrl:(NSString *)stampImageDataUrl
{
RNTPTDocumentView *documentView = self.documentViews[tag];
if (documentView) {
[documentView setStampImageData:annotationId pageNumber:pageNumber stampImageDataUrl:stampImageDataUrl];
} else {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Unable to get field for tag" userInfo:nil];
}
}


#pragma mark - Coordination

Expand Down
17 changes: 16 additions & 1 deletion ios/RNTPTDocumentViewModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -1409,5 +1409,20 @@ - (NSError *)errorFromException:(NSException *)exception
}

#pragma mark - Hygen Generated Methods

RCT_REMAP_METHOD(setStampImageData,
setStampImageDataForDocumentViewTag:(nonnull NSNumber *)tag
annotationId:(NSString *)annotationId
pageNumber:(NSInteger)pageNumber
stampImageDataUrl:(NSString *)stampImageDataUrl
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
@try {
[[self documentViewManager] setStampImageDataForDocumentViewTag:tag annotationId:annotationId pageNumber:pageNumber stampImageDataUrl:stampImageDataUrl];
resolve(nil);
}
@catch (NSException *exception) {
reject(@"set_stamp_image_data", @"Failed to set stamp image data", [self errorFromException:exception]);
}
}
@end
7 changes: 7 additions & 0 deletions lib/src/DocumentView/DocumentView.js
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,13 @@ export class DocumentView extends PureComponent {
return Promise.resolve();
};
// Hygen Generated Methods
setStampImageData = (annotationId, pageNumber, stampImageDataUrl) => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return DocumentViewManager.setStampImageData(tag, annotationId, pageNumber, stampImageDataUrl);
}
return Promise.resolve();
};
/**
* note: this function exists for supporting the old version. It simply calls setValuesForFields.
*
Expand Down
Loading

0 comments on commit f1c6880

Please sign in to comment.