Skip to content

Commit 4dbaeed

Browse files
authored
Merge pull request #77 from PSPDFKit/rad/import-export-XFDF
Add the ability to import and export from/to an XFDF file
2 parents 4742f93 + 92a3fb3 commit 4dbaeed

File tree

6 files changed

+172
-56
lines changed

6 files changed

+172
-56
lines changed

CordovaDemo/platforms/ios/www/plugins/pspdfkit-cordova-ios/PSPDFKitPlugin/pspdfkit.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ var PSPDFKitPlugin = new function() {
203203
setFormFieldValue: ['value', 'fullyQualifiedName', 'callback'],
204204
getFormFieldValue: ['fullyQualifiedName', 'callback'],
205205
});
206+
207+
// XFDF
208+
addMethods({
209+
importXFDF: ['xfdfPath', 'callback'],
210+
exportXFDF: ['xfdfPath', 'callback'],
211+
});
206212
};
207213
module.exports = PSPDFKitPlugin;
208214

PSPDFKitPlugin/PSPDFKitPlugin.m

Lines changed: 146 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ - (void)customBarButtonItemAction:(UIBarButtonItem *)sender {
385385
}
386386
}
387387

388-
- (NSURL *)pdfFileURLWithPath:(NSString *)path {
388+
- (NSURL *)fileURLWithPath:(NSString *)path {
389389
if (path) {
390390
path = [path stringByExpandingTildeInPath];
391391
path = [path stringByReplacingOccurrencesOfString:@"file:" withString:@""];
@@ -397,6 +397,42 @@ - (NSURL *)pdfFileURLWithPath:(NSString *)path {
397397
return nil;
398398
}
399399

400+
- (NSURL *)writableFileURLWithPath:(NSString *)path override:(BOOL)override copyIfNeeded:(BOOL)copyIfNeeded {
401+
NSURL *writableFileURL;
402+
if (path.absolutePath) {
403+
writableFileURL = [NSURL fileURLWithPath:path];
404+
} else {
405+
NSString *docsFolder = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
406+
writableFileURL = [NSURL fileURLWithPath:[docsFolder stringByAppendingPathComponent:path]];
407+
}
408+
409+
NSFileManager *fileManager = NSFileManager.defaultManager;
410+
if (override) {
411+
[fileManager removeItemAtURL:writableFileURL error:NULL];
412+
}
413+
414+
// If we don't have a writable file already, we move the provided file to the ~/Documents folder.
415+
if (![fileManager fileExistsAtPath:(NSString *)writableFileURL.path]) {
416+
// Create the folder where the writable file will be saved.
417+
NSError *createFolderError;
418+
if (![fileManager createDirectoryAtPath:writableFileURL.path.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:nil error:&createFolderError]) {
419+
NSLog(@"Failed to create directory: %@", createFolderError.localizedDescription);
420+
return nil;
421+
}
422+
423+
// Copy the provided file to a writable location if it exists.
424+
NSURL *fileURL = [self fileURLWithPath:path];
425+
NSError *copyError;
426+
if (copyIfNeeded && [fileManager fileExistsAtPath:(NSString *)fileURL.path]) {
427+
if (![fileManager copyItemAtURL:fileURL toURL:writableFileURL error:&copyError]) {
428+
NSLog(@"Failed to copy item at URL '%@' with error: %@", path, copyError.localizedDescription);
429+
return nil;
430+
}
431+
}
432+
}
433+
return writableFileURL;
434+
}
435+
400436
- (BOOL)isImagePath:(NSString *)path {
401437
NSString *pathExtension = path.pathExtension.lowercaseString;
402438
return [pathExtension isEqualToString:@"png"] || [pathExtension isEqualToString:@"jpeg"] || [pathExtension isEqualToString:@"jpg"];
@@ -409,7 +445,7 @@ - (void)configurePDFViewControllerWithPath:(NSString *)path options:(NSDictionar
409445

410446
if (path) {
411447
//configure document
412-
NSURL *url = [self pdfFileURLWithPath:path];
448+
NSURL *url = [self fileURLWithPath:path];
413449
if ([self isImagePath:path]) {
414450
_pdfDocument = [[PSPDFImageDocument alloc] initWithImageURL:url];
415451
}
@@ -435,25 +471,11 @@ - (void)configurePDFViewControllerWithPath:(NSString *)path options:(NSDictionar
435471
}
436472

437473
- (PSPDFDocument *)createXFDFDocumentWithPath:(NSString *)xfdfFilePath {
438-
NSURL *xfdfFileURL;
439-
if (xfdfFilePath.isAbsolutePath) {
440-
xfdfFileURL = [self pdfFileURLWithPath:xfdfFilePath];
441-
} else {
442-
// Locate the XFDF file in the ~/Documents directory
443-
NSString *docsFolder = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
444-
xfdfFileURL = [NSURL fileURLWithPath:[docsFolder stringByAppendingPathComponent:xfdfFilePath]];
445-
}
474+
// Copy the XFDF file to the ~/Documents foler or create one if we don't have one.
475+
NSURL *xfdfFileURL = [self writableFileURLWithPath:xfdfFilePath override:NO copyIfNeeded:YES];
446476

447477
// Create an XFDF file from the current document if one doesn't already exist.
448478
if (![NSFileManager.defaultManager fileExistsAtPath:(NSString *)xfdfFileURL.path]) {
449-
// Create the folder where the XFDF file will be saved.
450-
NSError *createFolderError;
451-
if (![NSFileManager.defaultManager createDirectoryAtPath:xfdfFileURL.path.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:nil error:&createFolderError]) {
452-
NSLog(@"Failed to create directory: %@", createFolderError.localizedDescription);
453-
return nil;
454-
}
455-
456-
// Write the file
457479
NSError *error;
458480
PSPDFFileDataSink *dataSink = [[PSPDFFileDataSink alloc] initWithFileURL:xfdfFileURL options:PSPDFDataSinkOptionNone error:&error];
459481
if (dataSink) {
@@ -729,7 +751,7 @@ - (void)setLicenseKey:(CDVInvokedUrlCommand *)command {
729751

730752
- (void)setFileURLForPSPDFDocumentWithJSON:(NSString *)path {
731753
// Brute-Force-Set.
732-
[_pdfDocument setValue:[self pdfFileURLWithPath:path] forKey:@"fileURL"];
754+
[_pdfDocument setValue:[self fileURLWithPath:path] forKey:@"fileURL"];
733755
}
734756

735757
- (NSString *)fileURLAsJSON {
@@ -1063,9 +1085,7 @@ - (void)presentWithXFDF:(CDVInvokedUrlCommand *)command {
10631085

10641086
// Validate the XFDF file path.
10651087
if (xfdfFilePath.length == 0) {
1066-
NSLog(@"The XFDF path must be a valid string");
1067-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]
1068-
callbackId:command.callbackId];
1088+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"The XFDF path must be a valid string."] callbackId:command.callbackId];
10691089
return;
10701090
}
10711091

@@ -1413,7 +1433,7 @@ - (void)getAnnotations:(CDVInvokedUrlCommand *)command {
14131433
if (annotationsJSON) {
14141434
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:@{@"annotations" : annotationsJSON}];
14151435
} else {
1416-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:@{@"localizedDescription": @"Failed to get annotations"}];
1436+
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Failed to get annotations."];
14171437
}
14181438

14191439
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
@@ -1427,9 +1447,7 @@ - (void)addAnnotation:(CDVInvokedUrlCommand *)command {
14271447
} else if ([jsonAnnotation isKindOfClass:NSDictionary.class]) {
14281448
data = [NSJSONSerialization dataWithJSONObject:jsonAnnotation options:0 error:nil];
14291449
} else {
1430-
NSLog(@"Invalid JSON Annotation.");
1431-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]
1432-
callbackId:command.callbackId];
1450+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid JSON Annotation."] callbackId:command.callbackId];
14331451
return;
14341452
}
14351453

@@ -1444,22 +1462,18 @@ - (void)addAnnotation:(CDVInvokedUrlCommand *)command {
14441462
}
14451463

14461464
if (success) {
1447-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK]
1465+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:YES]
14481466
callbackId:command.callbackId];
14491467
} else {
1450-
NSLog(@"Failed to add annotation.");
1451-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]
1452-
callbackId:command.callbackId];
1468+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Failed to add annotation."] callbackId:command.callbackId];
14531469
}
14541470
}
14551471

14561472
- (void)removeAnnotation:(CDVInvokedUrlCommand *)command {
14571473
id jsonAnnotation = [command argumentAtIndex:0];
14581474
NSString *annotationUUID = jsonAnnotation[@"uuid"];
14591475
if (annotationUUID.length == 0) {
1460-
NSLog(@"Invalid annotation UUID.");
1461-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]
1462-
callbackId:command.callbackId];
1476+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid annotation UUID."] callbackId:command.callbackId];
14631477
}
14641478

14651479
PSPDFDocument *document = self.pdfController.document;
@@ -1476,12 +1490,10 @@ - (void)removeAnnotation:(CDVInvokedUrlCommand *)command {
14761490
}
14771491

14781492
if (success) {
1479-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK]
1493+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:YES]
14801494
callbackId:command.callbackId];
14811495
} else {
1482-
NSLog(@"Failed to remove annotation.");
1483-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]
1484-
callbackId:command.callbackId];
1496+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Failed to remove annotation."] callbackId:command.callbackId];
14851497
}
14861498
}
14871499

@@ -1496,7 +1508,7 @@ - (void)getAllUnsavedAnnotations:(CDVInvokedUrlCommand *)command {
14961508
if (annotationsJSON) {
14971509
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:annotationsJSON];
14981510
} else {
1499-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:@{@"localizedDescription": @"Failed to get annotations"}];
1511+
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Failed to get unsaved annotations"];
15001512
}
15011513

15021514
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
@@ -1510,9 +1522,7 @@ - (void)applyInstantJSON:(CDVInvokedUrlCommand *)command {
15101522
} else if ([jsonValue isKindOfClass:NSDictionary.class]) {
15111523
data = [NSJSONSerialization dataWithJSONObject:jsonValue options:0 error:nil];
15121524
} else {
1513-
NSLog(@"Invalid JSON Annotations.");
1514-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]
1515-
callbackId:command.callbackId];
1525+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid Instant JSON payload."] callbackId:command.callbackId];
15161526
return;
15171527
}
15181528

@@ -1523,12 +1533,10 @@ - (void)applyInstantJSON:(CDVInvokedUrlCommand *)command {
15231533
BOOL success = [document applyInstantJSONFromDataProvider:dataContainerProvider toDocumentProvider:documentProvider lenient:NO error:NULL];
15241534
if (success) {
15251535
[self.pdfController reloadData];
1526-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK]
1536+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:YES]
15271537
callbackId:command.callbackId];
15281538
} else {
1529-
NSLog(@"Failed to add annotations.");
1530-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]
1531-
callbackId:command.callbackId];
1539+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Failed to add annotations."] callbackId:command.callbackId];
15321540
}
15331541
}
15341542

@@ -1560,9 +1568,7 @@ - (void)getFormFieldValue:(CDVInvokedUrlCommand *)command {
15601568
NSString *fullyQualifiedName = [command argumentAtIndex:0];
15611569

15621570
if (fullyQualifiedName.length == 0) {
1563-
NSLog(@"Invalid fully qualified name.");
1564-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]
1565-
callbackId:command.callbackId];
1571+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid fully qualified name."] callbackId:command.callbackId];
15661572
return;
15671573
}
15681574

@@ -1581,7 +1587,7 @@ - (void)getFormFieldValue:(CDVInvokedUrlCommand *)command {
15811587
if (formFieldValue) {
15821588
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:@{@"value": formFieldValue}];
15831589
} else {
1584-
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:@{@"localizedDescription": @"Failed to get annotations"}];
1590+
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Failed to get form field value."];
15851591
}
15861592

15871593
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
@@ -1592,27 +1598,30 @@ - (void)setFormFieldValue:(CDVInvokedUrlCommand *)command {
15921598
NSString *fullyQualifiedName = [command argumentAtIndex:1];
15931599

15941600
if (fullyQualifiedName.length == 0) {
1595-
NSLog(@"Invalid fully qualified name.");
1596-
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]
1597-
callbackId:command.callbackId];
1601+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid fully qualified name."] callbackId:command.callbackId];
15981602
return;
15991603
}
16001604

16011605
PSPDFDocument *document = self.pdfController.document;
16021606
VALIDATE_DOCUMENT(document)
16031607

1608+
BOOL success = NO;
16041609
for (PSPDFFormElement *formElement in document.formParser.forms) {
16051610
if ([formElement.fullyQualifiedFieldName isEqualToString:fullyQualifiedName]) {
16061611
if ([formElement isKindOfClass:PSPDFButtonFormElement.class]) {
16071612
if ([value isEqualToString:@"selected"]) {
16081613
[(PSPDFButtonFormElement *)formElement select];
1614+
success = YES;
16091615
} else if ([value isEqualToString:@"deselected"]) {
16101616
[(PSPDFButtonFormElement *)formElement deselect];
1617+
success = YES;
16111618
}
16121619
} else if ([formElement isKindOfClass:PSPDFChoiceFormElement.class]) {
16131620
((PSPDFChoiceFormElement *)formElement).selectedIndices = [NSIndexSet indexSetWithIndex:value.integerValue];
1621+
success = YES;
16141622
} else if ([formElement isKindOfClass:PSPDFTextFieldFormElement.class]) {
16151623
formElement.contents = value;
1624+
success = YES;
16161625
} else if ([formElement isKindOfClass:PSPDFSignatureFormElement.class]) {
16171626
NSLog(@"Signature form elements are not supported.");
16181627
} else {
@@ -1621,5 +1630,90 @@ - (void)setFormFieldValue:(CDVInvokedUrlCommand *)command {
16211630
break;
16221631
}
16231632
}
1633+
if (success) {
1634+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:YES]
1635+
callbackId:command.callbackId];
1636+
} else {
1637+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Failed to set form field value."] callbackId:command.callbackId];
1638+
}
1639+
}
1640+
1641+
#pragma mark - XFDF
1642+
1643+
- (void)importXFDF:(CDVInvokedUrlCommand *)command {
1644+
NSString *xfdfFilePath = [command argumentAtIndex:0];
1645+
// Validate the XFDF file path.
1646+
if (xfdfFilePath.length == 0) {
1647+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"The XFDF path must be a valid string."] callbackId:command.callbackId];
1648+
return;
1649+
}
1650+
1651+
NSURL *xfdfFileURL = [self fileURLWithPath:xfdfFilePath];
1652+
if (![NSFileManager.defaultManager fileExistsAtPath:(NSString *)xfdfFileURL.path]) {
1653+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"The XFDF file does not exist."] callbackId:command.callbackId];
1654+
return;
1655+
}
1656+
1657+
PSPDFDocument *document = self.pdfController.document;
1658+
VALIDATE_DOCUMENT(document)
1659+
1660+
PSPDFFileDataProvider *dataProvider = [[PSPDFFileDataProvider alloc] initWithFileURL:xfdfFileURL];
1661+
PSPDFXFDFParser *parser = [[PSPDFXFDFParser alloc] initWithDataProvider:dataProvider documentProvider:document.documentProviders[0]];
1662+
1663+
NSError *error;
1664+
if ([parser parseWithError:&error]) {
1665+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:YES]
1666+
callbackId:command.callbackId];
1667+
} else {
1668+
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
1669+
messageAsDictionary:@{@"localizedDescription": error.localizedDescription, @"domain": error.domain}];
1670+
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
1671+
return;
1672+
}
1673+
1674+
// Import annotations to the document.
1675+
NSArray <PSPDFAnnotation *> *annotations = parser.annotations;
1676+
if (annotations) {
1677+
[document addAnnotations:annotations options:nil];
1678+
}
16241679
}
1680+
1681+
- (void)exportXFDF:(CDVInvokedUrlCommand *)command {
1682+
NSString *xfdfFilePath = [command argumentAtIndex:0];
1683+
// Validate the XFDF file path.
1684+
if (xfdfFilePath.length == 0) {
1685+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]
1686+
callbackId:command.callbackId];
1687+
return;
1688+
}
1689+
1690+
// Always overwrite the XFDF file we export to.
1691+
NSURL *xfdfFileURL = [self writableFileURLWithPath:xfdfFilePath override:YES copyIfNeeded:NO];
1692+
PSPDFDocument *document = self.pdfController.document;
1693+
VALIDATE_DOCUMENT(document)
1694+
1695+
// Collect all existing annotations from the document
1696+
NSMutableArray *annotations = [NSMutableArray array];
1697+
for (NSArray *pageAnnotations in [document allAnnotationsOfType:PSPDFAnnotationTypeAll].allValues) {
1698+
[annotations addObjectsFromArray:pageAnnotations];
1699+
}
1700+
// Write to the XFDF file.
1701+
NSError *error;
1702+
PSPDFFileDataSink *dataSink = [[PSPDFFileDataSink alloc] initWithFileURL:xfdfFileURL options:PSPDFDataSinkOptionNone error:&error];
1703+
if (dataSink) {
1704+
if ([[PSPDFXFDFWriter new] writeAnnotations:annotations toDataSink:dataSink documentProvider:document.documentProviders[0] error:&error]) {
1705+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:YES]
1706+
callbackId:command.callbackId];
1707+
} else {
1708+
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
1709+
messageAsDictionary:@{@"localizedDescription": error.localizedDescription, @"domain": error.domain}];
1710+
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
1711+
}
1712+
} else {
1713+
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
1714+
messageAsDictionary:@{@"localizedDescription": error.localizedDescription, @"domain": error.domain}];
1715+
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
1716+
}
1717+
}
1718+
16251719
@end

PSPDFKitPlugin/pspdfkit.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,5 +203,10 @@ var PSPDFKitPlugin = new function() {
203203
getFormFieldValue: ['fullyQualifiedName', 'callback'],
204204
});
205205

206+
// XFDF
207+
addMethods({
208+
importXFDF: ['xfdfPath', 'callback'],
209+
exportXFDF: ['xfdfPath', 'callback'],
210+
});
206211
};
207212
module.exports = PSPDFKitPlugin;

0 commit comments

Comments
 (0)