├── .github └── workflows │ └── build.yml ├── .gitignore ├── Classes ├── ComPspdfkitModule.h ├── ComPspdfkitModule.m ├── ComPspdfkitModuleAssets.h ├── ComPspdfkitModuleAssets.m ├── ComPspdfkitPrefix.pch ├── ComPspdfkitView.h ├── ComPspdfkitView.m ├── ComPspdfkitViewProxy.h ├── ComPspdfkitViewProxy.m ├── PSPDFUtils.h ├── PSPDFUtils.m ├── TIPSPDFAnnotationProxy.h ├── TIPSPDFAnnotationProxy.m ├── TIPSPDFViewController.h ├── TIPSPDFViewController.m ├── TIPSPDFViewControllerProxy.h └── TIPSPDFViewControllerProxy.m ├── LICENSE ├── PSPDFKit.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ └── PSPDFKit.xcscheme ├── README.md ├── example ├── PSPDFKit.pdf ├── app.js └── protected.pdf ├── manifest ├── package.json ├── scripts ├── bootstrap.js └── templates │ ├── module.xcconfig │ ├── podfile │ └── titanium.xcconfig └── timodule.xml /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | # 4 | # THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | # AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | # UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | # This notice may not be removed from this file. 8 | # 9 | 10 | name: build 11 | on: pull_request 12 | 13 | jobs: 14 | macos: 15 | runs-on: macos-latest 16 | steps: 17 | # Check out the repository. 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | # Choose Xcode version. 21 | - name: Choose Xcode version 22 | run: sudo xcode-select -switch /Applications/Xcode_12.4.app 23 | # Choose Node version. 24 | - name: Choose Node version 25 | uses: actions/setup-node@v1 26 | with: { node-version: 12.x } 27 | # Install Node dependencies. 28 | - name: Install Node dependencies 29 | run: npm install 30 | # Install Titanium SDK. 31 | - name: Install Titanium SDK 32 | run: npx titanium sdk install $(node ./scripts/bootstrap.js versions --just titanium) 33 | # Build the module. 34 | - name: Build 35 | run: node ./scripts/bootstrap.js build --verbose 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS boilerplate. 2 | .DS_Store 3 | .Trashes 4 | .localized 5 | 6 | # Xcode boilerplate. 7 | xcuserdata 8 | *.pbxuser 9 | *.xccheckout 10 | *.xcuserstate 11 | 12 | # Build artifacts. 13 | build 14 | dist 15 | metadata.json 16 | module.xcconfig 17 | platform 18 | Podfile 19 | Podfile.lock 20 | Pods 21 | titanium.xcconfig 22 | 23 | # Do not ignore templates. 24 | !scripts/templates/* 25 | 26 | # Node.js artifacts. 27 | package-lock.json 28 | node_modules 29 | 30 | # Version manager artifacts. 31 | .nvmrc 32 | .tool-versions 33 | 34 | # PSPDFKit binaries. 35 | PSPDFKit.framework 36 | PSPDFKit.xcframework 37 | PSPDFKitUI.framework 38 | PSPDFKitUI.xcframework 39 | -------------------------------------------------------------------------------- /Classes/ComPspdfkitModule.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | /// PSPDFKit Titanium Bridge. 14 | @interface ComPspdfkitModule : TiModule 15 | 16 | __unused static BOOL PSTReplaceMethodWithBlock(Class c, SEL origSEL, SEL newSEL, id block); 17 | 18 | /// Get the version of PSPDFKit. (This is not the version of the module bridge) 19 | - (id)PSPDFKitVersion; 20 | 21 | /// Enable the license key. 22 | - (void)setLicenseKey:(id)license; 23 | 24 | /// Shows a modal page with the pdf. At least one argument (the pdf name) is needed. 25 | /// Argument 2 sets animated to true or false. 26 | /// Argument 3 can be an array with options for `PSPDFViewController`. 27 | /// See http://pspdfkit.com/documentation.html for details. You need to write the numeric equivalent for enumeration values (e.g. `PSPDFPageModeDouble` has the numeric value of 1) 28 | /// Argument 4 can be an array with options for PSPDFDocument. 29 | /// Returns a PSPDFViewController object. 30 | - (id)showPDFAnimated:(id)pathArray; 31 | 32 | /// Clears the whole application cache. No arguments needed. 33 | - (void)clearCache:(id)args; 34 | 35 | /// Starts caching the document in advance. Argument is filePath. 36 | - (void)cacheDocument:(id)args; 37 | 38 | /// Return a rendered image. 39 | /// Document | Page | Size (Full=0/Thumb=1) 40 | - (id)imageForDocument:(id)args; 41 | 42 | /// Stops caching the document in advance. Argument is filePath. 43 | - (void)stopCachingDocument:(id)args; 44 | 45 | /// Removes the cache for a specific document. Argument is filePath. 46 | - (void)removeCacheForDocument:(id)args; 47 | 48 | /// Allows setting custom language additions. 49 | /// Accepts a dictionary with language dictionaries mapped to the language string. 50 | - (void)setLanguageDictionary:(id)dictionary; 51 | 52 | /// Set internal log level. Defaults to PSPDFKit default. 53 | - (void)setLogLevel:(id)logLevel; 54 | 55 | @end 56 | 57 | @interface ComPspdfkitSourceModule : ComPspdfkitModule 58 | @end 59 | -------------------------------------------------------------------------------- /Classes/ComPspdfkitModule.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import "ComPspdfkitModule.h" 11 | 12 | #import "ComPspdfkitView.h" 13 | #import "PSPDFUtils.h" 14 | #import "TIPSPDFAnnotationProxy.h" 15 | #import "TIPSPDFViewController.h" 16 | #import "TIPSPDFViewControllerProxy.h" 17 | #import 18 | #import 19 | 20 | static BOOL PSTReplaceMethodWithBlock(Class c, SEL origSEL, SEL newSEL, id block) { 21 | NSCParameterAssert(c); 22 | NSCParameterAssert(origSEL); 23 | NSCParameterAssert(newSEL); 24 | NSCParameterAssert(block); 25 | 26 | if ([c instancesRespondToSelector:newSEL]) return YES; // Selector already implemented, skip silently. 27 | 28 | Method origMethod = class_getInstanceMethod(c, origSEL); 29 | 30 | // Add the new method. 31 | IMP impl = imp_implementationWithBlock(block); 32 | if (!class_addMethod(c, newSEL, impl, method_getTypeEncoding(origMethod))) { 33 | NSLog(@"Failed to add method: %@ on %@", NSStringFromSelector(newSEL), c); 34 | return NO; 35 | } else { 36 | Method newMethod = class_getInstanceMethod(c, newSEL); 37 | 38 | // If original doesn't implement the method we want to swizzle, create it. 39 | if (class_addMethod(c, origSEL, method_getImplementation(newMethod), method_getTypeEncoding(origMethod))) { 40 | class_replaceMethod(c, newSEL, method_getImplementation(origMethod), method_getTypeEncoding(newMethod)); 41 | } else { 42 | method_exchangeImplementations(origMethod, newMethod); 43 | } 44 | } 45 | return YES; 46 | } 47 | 48 | @interface TiRootViewController (PSPDFInternal) 49 | - (void)refreshOrientationWithDuration:(NSTimeInterval)duration; 50 | - (void)pspdf_refreshOrientationWithDuration:(NSTimeInterval)duration; // will be added dynamically 51 | @end 52 | 53 | @implementation ComPspdfkitModule 54 | 55 | /////////////////////////////////////////////////////////////////////////////////////////////////// 56 | #pragma mark - Appcelerator Lifecycle 57 | 58 | // this method is called when the module is first loaded 59 | - (void)startup { 60 | [super startup]; 61 | [self printVersionStringOnce]; 62 | 63 | // Appcelerator doesn't cope well with high memory usage. 64 | PSPDFKitGlobal.sharedInstance[@"com.pspdfkit.low-memory-mode"] = @YES; 65 | } 66 | 67 | // this method is called when the module is being unloaded 68 | // typically this is during shutdown. make sure you don't do too 69 | // much processing here or the app will be quit forcibly 70 | - (void)shutdown:(id)sender { 71 | [super shutdown:sender]; 72 | } 73 | 74 | - (id)moduleGUID { 75 | return @"3056f4e3-4ee6-4cf3-8417-1d8b8f95853c"; 76 | } 77 | 78 | - (NSString *)moduleId { 79 | return @"com.pspdfkit"; 80 | } 81 | 82 | /////////////////////////////////////////////////////////////////////////////////////////////////// 83 | #pragma mark - Private 84 | 85 | // Extract a dictionary from the input 86 | - (NSDictionary *)dictionaryFromInput:(NSArray *)input position:(NSUInteger)position { 87 | NSDictionary *dict = input.count > position && [input[position] isKindOfClass:NSDictionary.class] ? input[position] : nil; 88 | return dict; 89 | } 90 | 91 | // Show version string once in the console. 92 | - (void)printVersionStringOnce { 93 | static BOOL printVersionOnce = YES; 94 | static dispatch_once_t onceToken; 95 | dispatch_once(&onceToken, ^{ 96 | NSLog(@"Initialized PSPDFKit %@", [self PSPDFKitVersion]); 97 | printVersionOnce = NO; 98 | }); 99 | } 100 | 101 | // internal helper for pushing the PSPDFViewController on the view 102 | - (TIPSPDFViewControllerProxy *)pspdf_displayPdfInternal:(NSArray *)pdfNames animation:(NSUInteger)animation options:(NSDictionary *)options documentOptions:(NSDictionary *)documentOptions { 103 | __block TIPSPDFViewControllerProxy *proxy = nil; 104 | ps_dispatch_main_sync(^{ 105 | PSPDFDocument *document = nil; 106 | 107 | // Support encryption 108 | NSString *passphrase = documentOptions[@"passphrase"]; 109 | NSString *salt = documentOptions[@"salt"]; 110 | if (passphrase.length && salt.length) { 111 | NSURL *pdfURL = [NSURL fileURLWithPath:[pdfNames firstObject]]; 112 | PSPDFAESCryptoDataProvider *cryptoWrapper = [[PSPDFAESCryptoDataProvider alloc] initWithURL:pdfURL passphraseProvider:^NSString *{ 113 | return passphrase; 114 | } salt:salt rounds:PSPDFDefaultPBKDFNumberOfRounds]; 115 | document = [[PSPDFDocument alloc] initWithDataProviders:@[cryptoWrapper]]; 116 | } 117 | 118 | if (!document) { 119 | NSMutableArray *dataProviders = [NSMutableArray array]; 120 | for (NSString *pdfPath in pdfNames) { 121 | NSURL *pdfURL = [NSURL fileURLWithPath:pdfPath isDirectory:NO]; 122 | if ([pdfURL.pathExtension.lowercaseString isEqualToString:@"pdf"]) { 123 | PSPDFCoordinatedFileDataProvider *coordinatedFileDataProvider = [[PSPDFCoordinatedFileDataProvider alloc] initWithFileURL:pdfURL]; 124 | if (coordinatedFileDataProvider) { 125 | [dataProviders addObject:coordinatedFileDataProvider]; 126 | } 127 | } 128 | } 129 | document = [[PSPDFDocument alloc] initWithDataProviders:dataProviders]; 130 | } 131 | 132 | TIPSPDFViewController *pdfController = [[TIPSPDFViewController alloc] initWithDocument:document]; 133 | 134 | [PSPDFUtils applyOptions:options onObject:pdfController]; 135 | [PSPDFUtils applyOptions:documentOptions onObject:document]; 136 | 137 | UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:pdfController]; 138 | UIViewController *rootViewController = (UIViewController *)[([UIApplication sharedApplication].windows)[0] rootViewController]; 139 | 140 | // allow custom animation styles 141 | PSTiLog(@"animation: %d", animation); 142 | if (animation >= 2) { 143 | navController.modalTransitionStyle = animation - 2; 144 | } 145 | 146 | // encapsulate controller into proxy 147 | //PSTiLog(@"_pspdf_displayPdfInternal"); 148 | proxy = [[TIPSPDFViewControllerProxy alloc] initWithPDFController:pdfController context:self.pageContext parentProxy:self]; 149 | 150 | [rootViewController presentViewController:navController animated:animation > 0 completion:NULL]; 151 | }); 152 | return proxy; 153 | } 154 | 155 | /////////////////////////////////////////////////////////////////////////////////////////////////// 156 | #pragma mark - Public 157 | 158 | - (id)PSPDFKitVersion { 159 | return PSPDFKitGlobal.versionString; 160 | } 161 | 162 | - (void)setLicenseKey:(id)license { 163 | NSString *licenseString = [license isKindOfClass:NSArray.class] ? [license firstObject] : license; 164 | if ([licenseString isKindOfClass:NSString.class] && licenseString.length > 0) { 165 | if (![NSThread isMainThread]) { 166 | dispatch_sync(dispatch_get_main_queue(), ^{ 167 | [PSPDFKitGlobal setLicenseKey:licenseString]; 168 | }); 169 | } else { 170 | [PSPDFKitGlobal setLicenseKey:licenseString]; 171 | } 172 | } 173 | } 174 | 175 | /// show modal pdf animated 176 | - (id)showPDFAnimated:(NSArray *)pathArray { 177 | [self printVersionStringOnce]; 178 | 179 | if (pathArray.count < 1 || pathArray.count > 4 || ![pathArray[0] isKindOfClass:NSString.class] || [pathArray[0] length] == 0) { 180 | PSCLog(@"PSPDFKit Error. At least one argument is needed: pdf filename (either absolute or relative (application bundle and documents directory are searched for it)\n \ 181 | Argument 2 sets animated to true or false. (optional, defaults to true)\n \ 182 | Argument 3 can be an array with options for PSPDFViewController. See http://pspdfkit.com/documentation.html for details. You need to write the numeric equivalent for enumeration values (e.g. PSPDFPageModeDouble has the numeric value of 1)\ 183 | Argument 4 can be an array with options for PSPDFDocument.\ 184 | \n(arguments: %@)", pathArray); 185 | return nil; 186 | } 187 | 188 | NSUInteger animation = 1; // default modal 189 | if (pathArray.count >= 2 && [pathArray[1] isKindOfClass:NSNumber.class]) { 190 | animation = [pathArray[1] intValue]; 191 | } 192 | 193 | // be somewhat intelligent about path search 194 | id filePath = pathArray[0]; 195 | NSArray *pdfPaths = [PSPDFUtils resolvePaths:filePath]; 196 | 197 | // extract options from input 198 | NSDictionary *options = [self dictionaryFromInput:pathArray position:2]; 199 | NSDictionary *documentOptions = [self dictionaryFromInput:pathArray position:3]; 200 | 201 | if (options) PSCLog(@"options: %@", options); 202 | if (documentOptions) PSCLog(@"documentOptions: %@", documentOptions); 203 | 204 | PSCLog(@"Opening PSPDFViewController for %@.", pdfPaths); 205 | return [self pspdf_displayPdfInternal:pdfPaths animation:animation options:options documentOptions:documentOptions]; 206 | } 207 | 208 | - (void)clearCache:(id)args { 209 | PSCLog(@"requesting clear cache... (spins of async)"); 210 | 211 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 212 | [[PSPDFKitGlobal sharedInstance].cache clearCache]; 213 | }); 214 | } 215 | 216 | - (void)cacheDocument:(id)args { 217 | PSCLog(@"Request to cache document at path %@", args); 218 | 219 | // be somewhat intelligent about path search 220 | NSArray *documents = [PSPDFUtils documentsFromArgs:args]; 221 | for (PSPDFDocument *document in documents) { 222 | [[PSPDFKitGlobal sharedInstance].cache cacheDocument:document 223 | withPageSizes:@[[NSValue valueWithCGSize:CGSizeMake(170.f, 220.f)], [NSValue valueWithCGSize:UIScreen.mainScreen.bounds.size]]]; 224 | } 225 | } 226 | 227 | - (void)removeCacheForDocument:(id)args { 228 | PSCLog(@"Request to REMOVE cache for document at path %@", args); 229 | 230 | // be somewhat intelligent about path search 231 | NSArray *documents = [PSPDFUtils documentsFromArgs:args]; 232 | for (PSPDFDocument *document in documents) { 233 | [[PSPDFKitGlobal sharedInstance].cache removeCacheForDocument:document]; 234 | } 235 | } 236 | 237 | - (void)stopCachingDocument:(id)args { 238 | PSCLog(@"Request to STOP cache document at path %@", args); 239 | 240 | // be somewhat intelligent about path search 241 | NSArray *documents = [PSPDFUtils documentsFromArgs:args]; 242 | for (PSPDFDocument *document in documents) { 243 | [[PSPDFKitGlobal sharedInstance].cache stopCachingDocument:document]; 244 | } 245 | } 246 | 247 | - (id)imageForDocument:(id)args { 248 | PSCLog(@"Request image: %@", args); 249 | if ([args count] < 2) { 250 | PSCLog(@"Invalid number of arguments: %@", args); 251 | return nil; 252 | } 253 | UIImage *image = nil; 254 | 255 | PSPDFDocument *document = [PSPDFUtils documentsFromArgs:args].firstObject; 256 | NSUInteger page = [args[1] unsignedIntegerValue]; 257 | BOOL full = [args count] < 3 || [args[2] unsignedIntegerValue] == 0; 258 | CGSize thumbnailSize = CGSizeMake(170.f, 220.f); 259 | 260 | // be somewhat intelligent about path search 261 | if (document && page < [document pageCount]) { 262 | PSPDFMutableRenderRequest *renderRequest = [[PSPDFMutableRenderRequest alloc] initWithDocument:document]; 263 | renderRequest.pageIndex = page; 264 | renderRequest.imageSize = full ? UIScreen.mainScreen.bounds.size : thumbnailSize; 265 | image = [[PSPDFKitGlobal sharedInstance].cache imageForRequest:renderRequest imageSizeMatching:PSPDFCacheImageSizeMatchingDefault error:NULL]; 266 | 267 | if (!image) { 268 | CGSize size = full ? [[UIScreen mainScreen] bounds].size : thumbnailSize; 269 | image = [document imageForPageAtIndex:page size:size clippedToRect:CGRectZero annotations:nil options:nil error:NULL]; 270 | } 271 | } 272 | 273 | // if we use this directly, we get linker errors??? 274 | id proxy = nil; 275 | if (NSClassFromString(@"TiUIImageViewProxy")) { 276 | proxy = [NSClassFromString(@"TiUIImageViewProxy") new]; 277 | [proxy performSelector:@selector(setImage:) withObject:image]; 278 | } 279 | return proxy; 280 | } 281 | 282 | - (void)setLanguageDictionary:(id)dictionary { 283 | ENSURE_UI_THREAD(setLanguageDictionary, dictionary); 284 | 285 | if (![dictionary isKindOfClass:NSDictionary.class]) { 286 | PSCLog(@"PSPDFKit Error. Argument error, need dictionary with languages."); 287 | } 288 | 289 | PSPDFSetLocalizationDictionary(dictionary); 290 | } 291 | 292 | - (void)setLogLevel:(id)logLevel { 293 | ENSURE_UI_THREAD(setLogLevel, logLevel); 294 | 295 | [[PSPDFKitGlobal sharedInstance] setLogLevel:[PSPDFUtils intValue:logLevel]]; 296 | PSCLog(@"New Log level set to %d", PSPDFLogLevel); 297 | } 298 | 299 | @end 300 | 301 | @implementation ComPspdfkitSourceModule @end 302 | -------------------------------------------------------------------------------- /Classes/ComPspdfkitModuleAssets.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a generated file. Do not edit or your changes will be lost 3 | */ 4 | 5 | @interface ComPspdfkitModuleAssets : NSObject 6 | 7 | - (NSData *)moduleAsset; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /Classes/ComPspdfkitModuleAssets.m: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a generated file. Do not edit or your changes will be lost 3 | */ 4 | #import "ComPspdfkitModuleAssets.h" 5 | 6 | extern NSData* filterDataInRange(NSData* thedata, NSRange range); 7 | 8 | @implementation ComPspdfkitModuleAssets 9 | 10 | - (NSData *)moduleAsset 11 | { 12 | 13 | 14 | return nil; 15 | } 16 | 17 | - (NSData *)resolveModuleAsset:(NSString *)path 18 | { 19 | 20 | 21 | return nil; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Classes/ComPspdfkitPrefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import 11 | 12 | // HACK: Titanium defines this macro, JavaScriptCore does as well. 13 | #ifdef JSC_OBJC_API_ENABLED 14 | #undef JSC_OBJC_API_ENABLED 15 | #endif 16 | 17 | //#define kPSCLogEnabled 18 | #ifdef kPSCLogEnabled 19 | #define PSCLog(fmt, ...) NSLog((@"%s/%d " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 20 | #else 21 | #define PSCLog(...) 22 | #endif 23 | -------------------------------------------------------------------------------- /Classes/ComPspdfkitView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | @class TIPSPDFViewControllerProxy; 14 | 15 | @interface ComPspdfkitView : TiUIView 16 | @property (nonatomic) TIPSPDFViewControllerProxy *controllerProxy; 17 | @end 18 | 19 | @interface ComPspdfkitSourceView : ComPspdfkitView 20 | @end 21 | -------------------------------------------------------------------------------- /Classes/ComPspdfkitView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import "ComPspdfkitView.h" 11 | 12 | #import "PSPDFUtils.h" 13 | #import "TIPSPDFViewController.h" 14 | #import "TIPSPDFViewControllerProxy.h" 15 | #import 16 | 17 | @interface ComPspdfkitView () 18 | @property (nonatomic) UIViewController *navController; // UINavigationController or PSPDFViewController 19 | @end 20 | 21 | @implementation ComPspdfkitView 22 | 23 | /////////////////////////////////////////////////////////////////////////////////////////////////// 24 | #pragma mark - Private 25 | 26 | - (void)createControllerProxy { 27 | PSTiLog(@"createControllerProxy"); 28 | 29 | if (!_controllerProxy) { // self triggers creation 30 | NSArray *pdfPaths = [PSPDFUtils resolvePaths:[self.proxy valueForKey:@"filename"]]; 31 | NSMutableArray *dataProviders = [NSMutableArray array]; 32 | for (NSString *pdfPath in pdfPaths) { 33 | NSURL *pdfURL = [NSURL fileURLWithPath:pdfPath isDirectory:NO]; 34 | if ([pdfURL.pathExtension.lowercaseString isEqualToString:@"pdf"]) { 35 | PSPDFCoordinatedFileDataProvider *coordinatedFileDataProvider = [[PSPDFCoordinatedFileDataProvider alloc] initWithFileURL:pdfURL]; 36 | if (coordinatedFileDataProvider) { 37 | [dataProviders addObject:coordinatedFileDataProvider]; 38 | } 39 | } 40 | } 41 | 42 | PSPDFDocument *pdfDocument = [[PSPDFDocument alloc] initWithDataProviders:dataProviders]; 43 | TIPSPDFViewController *pdfController = [[TIPSPDFViewController alloc] initWithDocument:pdfDocument]; 44 | 45 | NSDictionary *options = [self.proxy valueForKey:@"options"]; 46 | [PSPDFUtils applyOptions:options onObject:pdfController]; 47 | [PSPDFUtils applyOptions:[self.proxy valueForKey:@"documentOptions"] onObject:pdfDocument]; 48 | 49 | // default-hide close button 50 | if (![self.proxy valueForKey:@"options"][@"leftBarButtonItems"]) { 51 | pdfController.navigationItem.leftBarButtonItems = @[]; 52 | } 53 | 54 | const BOOL navBarHidden = [[self.proxy valueForKey:@"options"][@"navBarHidden"] boolValue]; 55 | 56 | // Encapsulate controller into proxy. 57 | self.controllerProxy = [[TIPSPDFViewControllerProxy alloc] initWithPDFController:pdfController context:self.proxy.pageContext parentProxy:self.proxy]; 58 | 59 | if (!pdfController.configuration.useParentNavigationBar && !navBarHidden) { 60 | UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:pdfController]; 61 | 62 | // Support for tinting the navigation controller/bar 63 | if (options[@"barColor"]) { 64 | UIColor *barColor = [[TiColor colorNamed:options[@"barColor"]] _color]; 65 | if (barColor) { 66 | navController.navigationBar.tintColor = barColor; 67 | } 68 | } 69 | 70 | self.navController = navController; 71 | }else { 72 | self.navController = pdfController; 73 | } 74 | } 75 | } 76 | 77 | - (UIViewController *)closestViewControllerOfView:(UIView *)view { 78 | UIResponder *responder = view; 79 | while ((responder = [responder nextResponder])) { 80 | if ([responder isKindOfClass:UIViewController.class]) { 81 | break; 82 | } 83 | } 84 | return (UIViewController *)responder; 85 | } 86 | 87 | /// This is a band-aid for Titanium not setting up the view controller hierarchy 88 | /// correctly. If a view controller is detached, we will run into various issues 89 | /// with view controller management and presentation. This can be removed once 90 | /// https://github.com/appcelerator/titanium_mobile/issues/11651 is fixed. 91 | - (void)fixContainmentAmongAncestorsOfViewController:(UIViewController *)viewController { 92 | // Make sure that the root view controller is owned by Titanium. 93 | Class rootViewControllerClass = NSClassFromString(@"TiRootViewController"); 94 | UIViewController *rootViewController = [[[UIApplication sharedApplication] keyWindow] rootViewController]; 95 | if (rootViewControllerClass == Nil || rootViewController == nil || ![rootViewController isKindOfClass:rootViewControllerClass]) { 96 | return; 97 | } 98 | // Find the detached view controller. 99 | UIViewController *detachedViewController = viewController; 100 | while (detachedViewController.parentViewController != nil) { 101 | detachedViewController = detachedViewController.parentViewController; 102 | } 103 | // Root view controller is allowed to be detached. If we reach this, it means 104 | // that the view controller hierarchy is good or that we fixed it already. 105 | if (detachedViewController == rootViewController) { 106 | return; 107 | } 108 | // Find the closest parent view controller of the detached view controler. 109 | // This needs to be called on detached view controller's superview, as _it_ 110 | // is the closest view controller of its own view. 111 | UIViewController *closestParentViewController = [self closestViewControllerOfView:detachedViewController.view.superview]; 112 | if (closestParentViewController == nil) { 113 | return; 114 | } 115 | // Fix the containment by properly attaching the detached view controller. 116 | NSLog(@"Fixing view controller containment by adding %@ as a child of %@.", detachedViewController, closestParentViewController); 117 | [detachedViewController willMoveToParentViewController:closestParentViewController]; 118 | [closestParentViewController addChildViewController:detachedViewController]; 119 | // Run this again until we reach root view controller. 120 | [self fixContainmentAmongAncestorsOfViewController:closestParentViewController]; 121 | } 122 | 123 | /////////////////////////////////////////////////////////////////////////////////////////////////// 124 | #pragma mark - NSObject 125 | 126 | - (id)init { 127 | if ((self = [super init])) { 128 | PSTiLog(@"ComPspdfkitView init"); 129 | } 130 | return self; 131 | } 132 | 133 | - (void)dealloc { 134 | PSTiLog(@"ComPspdfkitView dealloc"); 135 | [self destroyViewControllerRelationship]; 136 | } 137 | 138 | - (void)didMoveToWindow { 139 | [super didMoveToWindow]; 140 | UIViewController *controller = [self closestViewControllerOfView:self]; 141 | if (controller) { 142 | if (self.window) { 143 | [controller addChildViewController:self.navController]; 144 | [self.navController didMoveToParentViewController:controller]; 145 | [self fixContainmentAmongAncestorsOfViewController:controller]; 146 | } else { 147 | [self destroyViewControllerRelationship]; 148 | } 149 | } 150 | } 151 | 152 | - (void)destroyViewControllerRelationship { 153 | if (self.navController.parentViewController) { 154 | [self.navController willMoveToParentViewController:nil]; 155 | [self.navController removeFromParentViewController]; 156 | } 157 | } 158 | 159 | /////////////////////////////////////////////////////////////////////////////////////////////////// 160 | #pragma mark - TiView 161 | 162 | - (TIPSPDFViewControllerProxy *)controllerProxy { 163 | PSTiLog(@"accessing controllerProxy"); 164 | 165 | if (!_controllerProxy) { 166 | if (!NSThread.isMainThread) { 167 | dispatch_sync(dispatch_get_main_queue(), ^{ 168 | [self createControllerProxy]; 169 | }); 170 | }else { 171 | [self createControllerProxy]; 172 | } 173 | } 174 | 175 | return _controllerProxy; 176 | } 177 | 178 | - (void)frameSizeChanged:(CGRect)frame bounds:(CGRect)bounds { 179 | PSTiLog(@"frameSizeChanged:%@ bounds:%@", NSStringFromCGRect(frame), NSStringFromCGRect(bounds)); 180 | 181 | // be sure our view is attached 182 | if (!self.controllerProxy.controller.view.window) { 183 | // creates controller lazy 184 | 185 | [self addSubview:_navController.view]; 186 | [TiUtils setView:_navController.view positionRect:bounds]; 187 | }else { 188 | // force controller reloading to adapt to new position 189 | [TiUtils setView:_navController.view positionRect:bounds]; 190 | [self.controllerProxy.controller reloadData]; 191 | } 192 | } 193 | 194 | @end 195 | 196 | @implementation ComPspdfkitSourceView 197 | @end 198 | -------------------------------------------------------------------------------- /Classes/ComPspdfkitViewProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | // Relays methods to the internal TIPSPDFViewControllerProxy. 14 | @interface ComPspdfkitViewProxy : TiViewProxy 15 | 16 | /// Returns the current page. 17 | - (id)page; 18 | 19 | /// Returns total pages count. 20 | - (id)totalPages; 21 | 22 | /// Scroll to a specific page. Argument 1 = integer, argument 2 = animated. (optional, defaults to YES) 23 | - (void)scrollToPage:(id)args; 24 | 25 | /// Change view mode argument 1 = integer, argument 2 = animated. (optional, defaults to YES) 26 | - (void)setViewMode:(id)args; 27 | 28 | /// Open search. 29 | - (void)searchForString:(id)args; 30 | 31 | /// Close controller. (argument 1 = animated) 32 | - (void)close:(id)args; 33 | 34 | /// Register a callback for the didTapOnAnnotation event. Return true if you manually use the annotation, else false. 35 | - (void)setDidTapOnAnnotationCallback:(id)args; 36 | 37 | /// Exposes a helper to change link annotation stroke width. Set to change. 38 | - (void)setLinkAnnotationStrokeWidth:(id)arg; 39 | 40 | /// Exposes a helper to change link annotation color. Set to change. 41 | - (void)setLinkAnnotationBorderColor:(id)arg; 42 | 43 | /// Exposes a helper to change link annotation highlight color. Set to change. 44 | - (void)setLinkAnnotationHighlightColor:(id)arg; 45 | 46 | /// Set list of editable annotation types. 47 | - (void)setEditableAnnotationTypes:(id)arg; 48 | 49 | /// Exposes helper to set `thumbnailController.filterOptions`. 50 | - (void)setThumbnailFilterOptions:(id)arg; 51 | 52 | /// Exposes helper to set `outlineBarButtonItem.availableControllerOptions`. 53 | - (void)setOutlineControllerFilterOptions:(id)arg; 54 | 55 | /// Document's menu actions. 56 | - (void)setAllowedMenuActions:(id)arg; 57 | 58 | /// Expose the scrollingEnabled property 59 | - (void)setScrollEnabled:(id)arg; 60 | 61 | /// Return document path. 62 | - (id)documentPath; 63 | 64 | // Save changed annotations. 65 | - (void)saveAnnotations:(id)args; 66 | 67 | /// PSPDFDocument's annotationSaveMode property. 68 | - (void)setAnnotationSaveMode:(id)args; 69 | 70 | /// Hide any visible popover. arg: animated YES/NO 71 | - (void)hidePopover:(id)args; 72 | 73 | /// Opens the PSPDFOutlineViewController 74 | - (void)showOutlineView:(id)arg; 75 | 76 | /// Opens the PSPDFSearchViewController 77 | - (void)showSearchView:(id)arg; 78 | 79 | /// Opens the PSPDFBrightnessViewController 80 | - (void)showBrightnessView:(id)arg; 81 | 82 | /// Opens the UIPrintInteractionController 83 | - (void)showPrintView:(id)arg; 84 | 85 | /// Opens the MFMailComposeViewController 86 | - (void)showEmailView:(id)arg; 87 | 88 | /// Open the PSPDFAnnotationToolbar 89 | - (void)showAnnotationView:(id)arg; 90 | 91 | /// Opens the UIDocumentInteractionController 92 | - (void)showOpenInView:(id)arg; 93 | 94 | /// Opens the UIActivityViewController 95 | - (void)showActivityView:(id)arg; 96 | 97 | /// Bookmark the current page 98 | - (void)bookmarkPage:(id)arg; 99 | 100 | 101 | @end 102 | 103 | @interface ComPspdfkitSourceViewProxy : ComPspdfkitViewProxy @end 104 | -------------------------------------------------------------------------------- /Classes/ComPspdfkitViewProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import "ComPspdfkitViewProxy.h" 11 | 12 | #import "ComPspdfkitView.h" 13 | #import "TIPSPDFViewController.h" 14 | #import "TIPSPDFViewControllerProxy.h" 15 | 16 | // ARCified helper 17 | #define PSPDF_ENSURE_UI_THREAD_0_ARGS \ 18 | do { \ 19 | _Pragma("clang diagnostic push") \ 20 | _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ 21 | ENSURE_UI_THREAD_0_ARGS \ 22 | _Pragma("clang diagnostic pop") \ 23 | } while (0); 24 | 25 | @interface ComPspdfkitViewProxy () 26 | @property (nonatomic) TIPSPDFViewControllerProxy *controllerProxy; 27 | @end 28 | 29 | @implementation ComPspdfkitViewProxy 30 | 31 | /////////////////////////////////////////////////////////////////////////////////////////////////// 32 | #pragma mark - Lifecycle 33 | 34 | - (void)dealloc { 35 | PSTiLog(@"dealloc: %@", self) 36 | } 37 | 38 | /////////////////////////////////////////////////////////////////////////////////////////////////// 39 | #pragma mark - Private 40 | 41 | - (ComPspdfkitView *)pdfView { 42 | return (ComPspdfkitView *)self.view; 43 | } 44 | 45 | /////////////////////////////////////////////////////////////////////////////////////////////////// 46 | #pragma mark - TiViewProxy 47 | 48 | - (void)viewDidAttach { 49 | PSPDF_ENSURE_UI_THREAD_0_ARGS 50 | PSTiLog(@"viewDidAttach: %@ %@", [self pdfView], [[self pdfView] controllerProxy]); 51 | 52 | self.controllerProxy = [[self pdfView] controllerProxy]; 53 | self.controllerProxy.viewProxy = self; // register viewProxy 54 | } 55 | 56 | - (void)viewDidDetach { 57 | PSPDF_ENSURE_UI_THREAD_0_ARGS 58 | PSTiLog(@"viewDidDetach"); 59 | 60 | // don't access pdfView - is already nil here! 61 | self.controllerProxy.viewProxy = nil; // deregister viewProxy 62 | self.controllerProxy = nil; 63 | } 64 | 65 | /////////////////////////////////////////////////////////////////////////////////////////////////// 66 | #pragma mark - Proxy Relay 67 | 68 | - (id)page { 69 | PSTiLog(@"page, thread: %@ (isMain:%d)", [NSThread currentThread], [NSThread isMainThread]); 70 | 71 | __block NSUInteger page = 0; 72 | if (![NSThread isMainThread]) { 73 | dispatch_sync(dispatch_get_main_queue(), ^{ 74 | page = [[self page] unsignedIntegerValue]; 75 | }); 76 | }else { 77 | page = [[[[self pdfView] controllerProxy] page] unsignedIntegerValue]; 78 | } 79 | 80 | return @(page); 81 | } 82 | 83 | - (id)totalPages { 84 | PSTiLog(@"totalPages, thread: %@ (isMain:%d)", [NSThread currentThread], [NSThread isMainThread]); 85 | 86 | __block NSUInteger totalPages = 0; 87 | if (![NSThread isMainThread]) { 88 | dispatch_sync(dispatch_get_main_queue(), ^{ 89 | totalPages = [[self totalPages] unsignedIntegerValue]; 90 | }); 91 | }else { 92 | totalPages = [[[[self pdfView] controllerProxy] totalPages] unsignedIntegerValue]; 93 | } 94 | 95 | return @(totalPages); 96 | } 97 | 98 | - (void)scrollToPage:(id)args { 99 | ENSURE_UI_THREAD(scrollToPage, args); 100 | [[[self pdfView] controllerProxy] scrollToPage:args]; 101 | } 102 | 103 | - (void)setViewMode:(id)args { 104 | ENSURE_UI_THREAD(setViewMode, args); 105 | [[[self pdfView] controllerProxy] setViewMode:args]; 106 | } 107 | 108 | - (void)searchForString:(id)args { 109 | ENSURE_UI_THREAD(searchForString, args); 110 | [[[self pdfView] controllerProxy] searchForString:args]; 111 | } 112 | 113 | - (void)close:(id)args { 114 | ENSURE_UI_THREAD(close, args); 115 | [[[self pdfView] controllerProxy] close:args]; 116 | } 117 | 118 | - (void)setDidTapOnAnnotationCallback:(id)args { 119 | ENSURE_UI_THREAD(setDidTapOnAnnotationCallback, args); 120 | [[[self pdfView] controllerProxy] setDidTapOnAnnotationCallback:args]; 121 | } 122 | 123 | - (void)setLinkAnnotationStrokeWidth:(id)arg { 124 | ENSURE_UI_THREAD(setLinkAnnotationStrokeWidth, arg); 125 | [[[self pdfView] controllerProxy] setLinkAnnotationStrokeWidth:arg]; 126 | } 127 | 128 | - (void)setLinkAnnotationBorderColor:(id)arg { 129 | ENSURE_UI_THREAD(setLinkAnnotationBorderColor, arg); 130 | [[[self pdfView] controllerProxy] setLinkAnnotationBorderColor:arg]; 131 | } 132 | 133 | - (void)setLinkAnnotationHighlightColor:(id)arg { 134 | ENSURE_UI_THREAD(setLinkAnnotationHighlightColor, arg); 135 | [[[self pdfView] controllerProxy] setLinkAnnotationHighlightColor:arg]; 136 | } 137 | 138 | - (void)setThumbnailFilterOptions:(id)arg { 139 | ENSURE_UI_THREAD(setThumbnailFilterOptions, arg); 140 | [[[self pdfView] controllerProxy] setThumbnailFilterOptions:arg]; 141 | } 142 | 143 | - (void)setOutlineControllerFilterOptions:(id)arg { 144 | ENSURE_UI_THREAD(setOutlineControllerFilterOptions, arg); 145 | [[[self pdfView] controllerProxy] setOutlineControllerFilterOptions:arg]; 146 | } 147 | 148 | - (void)setAllowedMenuActions:(id)arg { 149 | ENSURE_UI_THREAD(setAllowedMenuActions, arg); 150 | [[[self pdfView] controllerProxy] setAllowedMenuActions:arg]; 151 | } 152 | 153 | - (void)setEditableAnnotationTypes:(id)arg { 154 | ENSURE_UI_THREAD(setEditableAnnotationTypes, arg); 155 | [[[self pdfView] controllerProxy] setEditableAnnotationTypes:arg]; 156 | } 157 | 158 | - (void)setScrollEnabled:(id)arg { 159 | ENSURE_UI_THREAD(setScrollEnabled, arg); 160 | [[[self pdfView] controllerProxy] setScrollEnabled:arg]; 161 | } 162 | 163 | - (id)documentPath { 164 | __block NSString *documentPath; 165 | if (![NSThread isMainThread]) { 166 | dispatch_sync(dispatch_get_main_queue(), ^{ 167 | documentPath = [[self documentPath] copy]; 168 | }); 169 | }else { 170 | documentPath = [[[self pdfView] controllerProxy] documentPath]; 171 | } 172 | return documentPath; 173 | } 174 | 175 | - (void)saveAnnotations:(id)args { 176 | ENSURE_UI_THREAD(saveAnnotations, args); 177 | [[[self pdfView] controllerProxy] saveAnnotations:args]; 178 | } 179 | 180 | - (void)setAnnotationSaveMode:(id)args { 181 | ENSURE_UI_THREAD(setAnnotationSaveMode, args); 182 | [[[self pdfView] controllerProxy] setAnnotationSaveMode:args]; 183 | } 184 | 185 | - (void)hidePopover:(id)args { 186 | ENSURE_UI_THREAD(hidePopover, args); 187 | [[[self pdfView] controllerProxy] hidePopover:args]; 188 | } 189 | 190 | - (void)showOutlineView:(id)arg { 191 | ENSURE_UI_THREAD(showOutlineView, arg); 192 | [[[self pdfView] controllerProxy] showOutlineView:arg]; 193 | } 194 | 195 | - (void)showSearchView:(id)arg { 196 | ENSURE_UI_THREAD(showSearchView, arg); 197 | [[[self pdfView] controllerProxy] showSearchView:arg]; 198 | } 199 | 200 | - (void)showBrightnessView:(id)arg { 201 | ENSURE_UI_THREAD(showBrightnessView, arg); 202 | [[[self pdfView] controllerProxy] showBrightnessView:arg]; 203 | } 204 | 205 | - (void)showPrintView:(id)arg { 206 | ENSURE_UI_THREAD(showPrintView, arg); 207 | [[[self pdfView] controllerProxy] showPrintView:arg]; 208 | } 209 | 210 | - (void)showEmailView:(id)arg { 211 | ENSURE_UI_THREAD(showEmailView, arg); 212 | [[[self pdfView] controllerProxy] showEmailView:arg]; 213 | } 214 | 215 | - (void)showAnnotationView:(id)arg { 216 | ENSURE_UI_THREAD(showAnnotationView, arg); 217 | [[[self pdfView] controllerProxy] showAnnotationView:arg]; 218 | } 219 | 220 | - (void)showOpenInView:(id)arg { 221 | ENSURE_UI_THREAD(showOpenInView, arg); 222 | [[[self pdfView] controllerProxy] showOpenInView:arg]; 223 | } 224 | 225 | - (void)showActivityView:(id)arg { 226 | ENSURE_UI_THREAD(showActivityView, arg); 227 | [[[self pdfView] controllerProxy] showActivityView:arg]; 228 | } 229 | 230 | - (void)bookmarkPage:(id)arg { 231 | ENSURE_UI_THREAD(bookmarkPage, arg); 232 | [[[self pdfView] controllerProxy] bookmarkPage:arg]; 233 | } 234 | 235 | @end 236 | 237 | @implementation ComPspdfkitSourceViewProxy 238 | @end 239 | -------------------------------------------------------------------------------- /Classes/PSPDFUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | @class PSPDFDocument; 14 | 15 | FOUNDATION_EXTERN void (^pst_targetActionBlock(id target, SEL action))(id); 16 | 17 | // Helper class for argument parsing. 18 | @interface PSPDFUtils : NSObject 19 | 20 | // Returns an integer from an argument array. 21 | + (NSInteger)intValue:(id)args; 22 | 23 | // Returns an integer from an argument array, on a specific position. Position starts at 0. Returns `NSNotFound` if invalid. 24 | + (NSInteger)intValue:(id)args onPosition:(NSUInteger)position; 25 | 26 | // Returns a float from an argument array. 27 | + (CGFloat)floatValue:(id)args; 28 | 29 | // Returns a float from an argument array, on a specific position. Position starts at 0. Returns `NSNotFound` if invalid. 30 | + (CGFloat)floatValue:(id)args onPosition:(NSUInteger)position; 31 | 32 | // Uses KVO to set an option on an object. 33 | + (void)applyOptions:(NSDictionary *)options onObject:(id)object; 34 | 35 | // Accept both NSString and NSArray. 36 | + (NSArray *)resolvePaths:(id)filePaths; 37 | 38 | // Returns `PSPDFDocument's` if the file could be resolved and exists. 39 | + (NSArray *)documentsFromArgs:(id)args; 40 | 41 | // Returns color from the first argument. 42 | + (UIColor *)colorFromArg:(id)arg; 43 | 44 | @end 45 | 46 | // Helper 47 | FOUNDATION_EXTERN id PSSafeCast(id object, Class targetClass); 48 | FOUNDATION_EXTERN void ps_dispatch_main_sync(dispatch_block_t block); 49 | FOUNDATION_EXTERN void ps_dispatch_main_async(dispatch_block_t block); 50 | FOUNDATION_EXTERN NSString *PSFixIncorrectPath(NSString *path); 51 | FOUNDATION_EXTERN UIView *PSViewInsideViewWithPrefix(UIView *view, NSString *classNamePrefix); 52 | 53 | 54 | // Private extensions inside PSPDFKit. 55 | @interface NSObject (PSPDFKitAdditions) 56 | 57 | // Register block to be called when `self` is deallocated. 58 | // If `owner` is not nil, block will be removed. 59 | - (NSString *)pspdf_addDeallocBlock:(dispatch_block_t)block owner:(id)owner; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Classes/PSPDFUtils.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import "PSPDFUtils.h" 11 | 12 | @implementation PSPDFUtils 13 | 14 | + (NSInteger)intValue:(id)args { 15 | return [self intValue:args onPosition:0]; 16 | } 17 | 18 | + (NSInteger)intValue:(id)args onPosition:(NSUInteger)position { 19 | NSInteger intValue = NSNotFound; 20 | 21 | if (position == 0 && [args isKindOfClass:NSNumber.class]) { 22 | intValue = [args intValue]; 23 | }else if([args isKindOfClass:NSArray.class] && [args count] > position) { 24 | intValue = [args[position] intValue]; 25 | } 26 | 27 | return intValue; 28 | } 29 | 30 | + (CGFloat)floatValue:(id)args { 31 | return [self floatValue:args onPosition:0]; 32 | } 33 | 34 | + (CGFloat)floatValue:(id)args onPosition:(NSUInteger)position { 35 | CGFloat floatValue = NSNotFound; 36 | 37 | if (position == 0 && [args isKindOfClass:NSNumber.class]) { 38 | floatValue = [args floatValue]; 39 | }else if([args isKindOfClass:NSArray.class] && [args count] > position) { 40 | floatValue = [args[position] floatValue]; 41 | } 42 | 43 | return floatValue; 44 | } 45 | 46 | + (UIColor *)colorFromArg:(id)arg { 47 | if ([arg isKindOfClass:NSArray.class] && [[arg firstObject] isEqual:@"clear"]) { 48 | return [UIColor clearColor]; 49 | }else { 50 | return [[TiUtils colorValue:arg] color]; 51 | } 52 | } 53 | 54 | // use KVO to apply options 55 | + (void)applyOptions:(NSDictionary *)options onObject:(id)object { 56 | if (!options || !object) return; 57 | 58 | __block BOOL isControllerNeedsReload = NO; 59 | // set options 60 | [options enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { 61 | BOOL processed = NO; 62 | @try { 63 | PSCLog(@"setting %@ to %@.", key, obj); 64 | 65 | // convert boolean to YES/NO 66 | if ([value isEqual:@"YES"]) value = @YES; 67 | else if([value isEqual:@"NO"]) value = @NO; 68 | 69 | // convert color 70 | if ([key rangeOfString:@"color" options:NSCaseInsensitiveSearch].length > 0) { 71 | value = [[TiColor colorNamed:value] _color]; 72 | } 73 | 74 | // special handling for toolbar 75 | if ([key hasSuffix:@"BarButtonItems"] && [value isKindOfClass:NSArray.class]) { 76 | NSMutableArray *newArray = [NSMutableArray array]; 77 | for (id arrayItem in value) { 78 | if ([arrayItem isKindOfClass:NSString.class]) { 79 | if ([object respondsToSelector:NSSelectorFromString(arrayItem)]) { 80 | [newArray addObject:[object valueForKey:arrayItem]]; 81 | } 82 | } else { 83 | id newArrayItem = arrayItem; 84 | if (![arrayItem isKindOfClass:UIBarButtonItem.class] && [arrayItem respondsToSelector:@selector(barButtonItem)]) { 85 | newArrayItem = [arrayItem performSelector:@selector(barButtonItem)]; 86 | // Try to retain the TIButton proxy. 87 | if ([arrayItem respondsToSelector:@selector(rememberSelf)]) { 88 | [arrayItem performSelector:@selector(rememberSelf)]; 89 | // Release proxy once `object` is deallocated. 90 | // (Object will be the PSPDFViewController) 91 | [object pspdf_addDeallocBlock:^{ 92 | [arrayItem performSelector:@selector(forgetSelf)]; 93 | } owner:arrayItem]; 94 | } 95 | } 96 | [newArray addObject:newArrayItem]; 97 | } 98 | } 99 | value = newArray; 100 | } 101 | 102 | // special case handling for annotation name list 103 | if ([key isEqual:@"editableAnnotationTypes"] && [value isKindOfClass:NSArray.class]) { 104 | value = [NSMutableSet setWithArray:value]; 105 | } 106 | 107 | else if ([key.lowercaseString hasSuffix:@"size"] && [value isKindOfClass:NSArray.class] && [value count] == 2) { 108 | value = [NSValue valueWithCGSize:CGSizeMake([[value objectAtIndex:0] floatValue], [[value objectAtIndex:1] floatValue])]; 109 | } 110 | 111 | else if ([key isEqual:@"navBarHidden"]) { 112 | // handled in -[ComPspdfkitView createControllerProxy] 113 | return; 114 | } 115 | 116 | // There is no password property since this is a write-only property. 117 | if ([object isKindOfClass:PSPDFDocument.class]) { 118 | PSPDFDocument *document = (PSPDFDocument *)object; 119 | 120 | // We support `password` as a property even though the call is different. 121 | if ([key isEqual:@"password"]) { 122 | [document unlockWithPassword:value]; 123 | processed = YES; 124 | // Allow to customize the URL handler for special links. 125 | // Should be in format protocolStrings = "myProtocol://,myprotocol://" (comma is the separator, no spaces!) 126 | } else if ([key isEqual:@"protocolStrings"]) { 127 | NSArray *protocolStrings = [value componentsSeparatedByString:@","]; 128 | [document setDidCreateDocumentProviderBlock:^(PSPDFDocumentProvider *pdfDocumentProvider) { 129 | pdfDocumentProvider.annotationManager.protocolStrings = protocolStrings; 130 | }]; 131 | // Ensure document providers are re-set. 132 | [document clearCache]; 133 | processed = YES; 134 | } 135 | } 136 | 137 | PSCLog(@"Set %@ to %@", key, obj); 138 | 139 | if (!processed) { 140 | if ([object respondsToSelector:NSSelectorFromString(key)]) { 141 | [object setValue:value forKeyPath:key]; 142 | } else { 143 | if ([object isKindOfClass:PSPDFViewController.class]) { 144 | PSPDFViewController *ctrl = object; 145 | // special handling for toolbar 146 | if ([key hasSuffix:@"BarButtonItems"] && [value isKindOfClass:NSArray.class]) { 147 | PSPDFNavigationItem *navigationItem = ctrl.navigationItem; 148 | NSArray *barButtonItems = (NSArray *)value; 149 | NSArray * (^removeItems)(NSArray *, NSArray *) = ^(NSArray *barButtonItems, NSArray *itemsToRemove) { 150 | NSMutableArray *mutableBarButtonItems = barButtonItems.mutableCopy; 151 | [mutableBarButtonItems removeObjectsInArray:itemsToRemove]; 152 | return mutableBarButtonItems; 153 | }; 154 | if ([key isEqualToString:@"leftBarButtonItems"]) { 155 | navigationItem.rightBarButtonItems = removeItems(navigationItem.rightBarButtonItems, barButtonItems); 156 | navigationItem.leftBarButtonItems = barButtonItems; 157 | if ([barButtonItems containsObject:ctrl.closeButtonItem]) { 158 | navigationItem.closeBarButtonItem = nil; 159 | } 160 | } 161 | if ([key isEqualToString:@"rightBarButtonItems"]) { 162 | navigationItem.leftBarButtonItems = removeItems(navigationItem.leftBarButtonItems, barButtonItems); 163 | navigationItem.rightBarButtonItems = barButtonItems.reverseObjectEnumerator.allObjects; 164 | } 165 | } else { 166 | // set value via PSPDFConfiguration 167 | [ctrl updateConfigurationWithoutReloadingWithBuilder:^(PSPDFConfigurationBuilder *builder) { 168 | @try { 169 | [builder setValue:value forKey:key]; 170 | } 171 | @catch (NSException *exception) { 172 | PSCLog(@"Warning! Unable to set %@ for %@.", obj, key); 173 | } 174 | }]; 175 | isControllerNeedsReload = YES; 176 | } 177 | } 178 | } 179 | } 180 | } 181 | @catch (NSException *exception) { 182 | PSCLog(@"Recovered from error while parsing options: %@", exception); 183 | } 184 | }]; 185 | if (isControllerNeedsReload && [object isKindOfClass:PSPDFViewController.class]) { 186 | PSPDFViewController *ctrl = object; 187 | [ctrl reloadData]; 188 | } 189 | } 190 | 191 | // be smart about path search 192 | + (NSArray *)resolvePaths:(id)filePaths { 193 | NSMutableArray *resolvedPaths = [NSMutableArray array]; 194 | 195 | if ([filePaths isKindOfClass:[NSString class]]) { 196 | NSString *resolvedPath = [self resolvePath:(NSString *)filePaths]; 197 | if(resolvedPath) [resolvedPaths addObject:resolvedPath]; 198 | }else if([filePaths isKindOfClass:NSArray.class]) { 199 | for (NSString *filePath in filePaths) { 200 | NSString *resolvedPath = [self resolvePath:filePath]; 201 | if(resolvedPath) [resolvedPaths addObject:resolvedPath]; 202 | } 203 | } 204 | 205 | return resolvedPaths; 206 | } 207 | 208 | + (NSString *)resolvePath:(NSString *)filePath { 209 | if (![filePath isKindOfClass:NSString.class]) return nil; 210 | 211 | // If this is a full path; don't try to replace any parts. 212 | if (filePath.isAbsolutePath) { 213 | return PSFixIncorrectPath(filePath); 214 | } 215 | 216 | NSString *pdfPath = filePath; 217 | NSFileManager *fileManager = [NSFileManager new]; 218 | 219 | if (![fileManager fileExistsAtPath:filePath]) { 220 | // Convert to URL and back to cope with file://localhost paths 221 | NSURL *urlPath = [NSURL URLWithString:filePath]; 222 | pdfPath = [urlPath path]; 223 | //PSTiLog(@"converted: %@", urlPath.path); 224 | if (![fileManager fileExistsAtPath:pdfPath]) { 225 | // try application bundle 226 | pdfPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:filePath]; 227 | 228 | // try documents directory 229 | if (![fileManager fileExistsAtPath:pdfPath]) { 230 | NSString *cacheFolder = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; 231 | pdfPath = [cacheFolder stringByAppendingPathComponent:filePath]; 232 | if (![fileManager fileExistsAtPath:pdfPath]) { 233 | PSCLog(@"PSPDFKit Error: pdf '%@' could not be found. Searched native path, application bundle and documents directory.", filePath); 234 | } 235 | } 236 | } 237 | } 238 | return pdfPath; 239 | } 240 | 241 | + (NSArray *)documentsFromArgs:(id)args { 242 | NSMutableArray *documents = [NSMutableArray array]; 243 | 244 | // be somewhat intelligent about path search 245 | for (NSString *filePath in args) { 246 | if ([filePath isKindOfClass:NSString.class]) { 247 | NSString *pdfPath = [PSPDFUtils resolvePath:filePath]; 248 | 249 | if (pdfPath.length && [[NSFileManager defaultManager] fileExistsAtPath:pdfPath]) { 250 | PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:[NSURL fileURLWithPath:pdfPath]]; 251 | if (document) { 252 | [documents addObject:document]; 253 | } 254 | } 255 | } 256 | } 257 | return documents; 258 | } 259 | 260 | @end 261 | 262 | void ps_dispatch_sync_if(dispatch_queue_t queue, BOOL sync, dispatch_block_t block) { 263 | sync ? dispatch_sync(queue, block) : block(); 264 | } 265 | 266 | void ps_dispatch_async_if(dispatch_queue_t queue, BOOL async, dispatch_block_t block) { 267 | async ? dispatch_async(queue, block) : block(); 268 | } 269 | 270 | void ps_dispatch_main_sync(dispatch_block_t block) { 271 | ps_dispatch_sync_if(dispatch_get_main_queue(), !NSThread.isMainThread, block); 272 | } 273 | 274 | void ps_dispatch_main_async(dispatch_block_t block) { 275 | ps_dispatch_async_if(dispatch_get_main_queue(), !NSThread.isMainThread, block); 276 | } 277 | 278 | BOOL PSIsIncorrectPath(NSString *path) { 279 | return [path hasPrefix:@"file://localhost"]; 280 | } 281 | 282 | NSString *PSFixIncorrectPath(NSString *path) { 283 | // If string is wrongly converted from an NSURL internally (via description and not path), fix this problem silently. 284 | NSString *newPath = path; 285 | if (PSIsIncorrectPath(path)) newPath = ((NSURL *)[NSURL URLWithString:path]).path; 286 | return newPath; 287 | } 288 | 289 | UIView *PSViewInsideViewWithPrefix(UIView *view, NSString *classNamePrefix) { 290 | if (!view || classNamePrefix.length == 0) return nil; 291 | 292 | UIView *theView = nil; 293 | for (UIView *subview in view.subviews) { 294 | if ([NSStringFromClass(subview.class) hasPrefix:classNamePrefix] || [NSStringFromClass(subview.superclass) hasPrefix:classNamePrefix]) { 295 | return subview; 296 | }else { 297 | if ((theView = PSViewInsideViewWithPrefix(subview, classNamePrefix))) break; 298 | } 299 | } 300 | return theView; 301 | } 302 | 303 | #define PSPDF_SILENCE_CALL_TO_UNKNOWN_SELECTOR(expression) \ 304 | _Pragma("clang diagnostic push") \ 305 | _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ 306 | expression \ 307 | _Pragma("clang diagnostic pop") 308 | 309 | #define PSPDFWeakifyAs(object, weakName) typeof(object) __weak weakName = object 310 | 311 | void (^pst_targetActionBlock(id target, SEL action))(id) { 312 | // If there's no target, return an empty block. 313 | if (!target) return ^(__unused id sender) {}; 314 | 315 | NSCParameterAssert(action); 316 | 317 | // All ObjC methods have two arguments. This fails if either target is nil, action not implemented or else. 318 | NSUInteger numberOfArguments = [target methodSignatureForSelector:action].numberOfArguments; 319 | NSCAssert(numberOfArguments == 2 || numberOfArguments == 3, @"%@ should have at most one argument.", NSStringFromSelector(action)); 320 | 321 | PSPDFWeakifyAs(target, weakTarget); 322 | if (numberOfArguments == 2) { 323 | return ^(__unused id sender) { PSPDF_SILENCE_CALL_TO_UNKNOWN_SELECTOR([weakTarget performSelector:action];) }; 324 | } else { 325 | return ^(id sender) { PSPDF_SILENCE_CALL_TO_UNKNOWN_SELECTOR([weakTarget performSelector:action withObject:sender];) }; 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /Classes/TIPSPDFAnnotationProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | @interface TIPSPDFAnnotationProxy : TiProxy 14 | 15 | /// Initializes annotation proxy. 16 | - (instancetype)initWithAnnotation:(PSPDFAnnotation *)annotation; 17 | 18 | /// Link if target is a website. 19 | @property (nonatomic, readonly) NSString *siteLinkTarget; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Classes/TIPSPDFAnnotationProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import "TIPSPDFAnnotationProxy.h" 11 | 12 | @interface TIPSPDFAnnotationProxy() 13 | @property (nonatomic) PSPDFAnnotation *annotation; 14 | @end 15 | 16 | @implementation TIPSPDFAnnotationProxy 17 | 18 | /////////////////////////////////////////////////////////////////////////////////////////////////// 19 | #pragma mark - NSObject 20 | 21 | - (id)initWithAnnotation:(PSPDFAnnotation *)annotation { 22 | if ((self = [super init])) { 23 | _annotation = annotation; 24 | } 25 | return self; 26 | } 27 | 28 | /////////////////////////////////////////////////////////////////////////////////////////////////// 29 | #pragma mark - Public 30 | 31 | - (NSString *)siteLinkTarget { 32 | if ([self.annotation isKindOfClass:[PSPDFLinkAnnotation class]]) { 33 | return ((PSPDFLinkAnnotation *)self.annotation).URL.absoluteString; 34 | }else { 35 | return nil; 36 | } 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Classes/TIPSPDFViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | // uncomment to enable logging 14 | //#define PSTiLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 15 | #define PSTiLog(fmt, ...) 16 | 17 | @class TIPSPDFViewControllerProxy; 18 | 19 | /// Subclass of `PSPDFViewController` that enables sending events to Appcelerator. 20 | @interface TIPSPDFViewController : PSPDFViewController 21 | 22 | /// Close controller, optionally animated. 23 | - (void)closeControllerAnimated:(BOOL)animated; 24 | 25 | @property (nonatomic) TIPSPDFViewControllerProxy *proxy; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Classes/TIPSPDFViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import "TIPSPDFViewController.h" 11 | 12 | #import "ComPspdfkitModule.h" 13 | #import "TIPSPDFViewControllerProxy.h" 14 | #import 15 | 16 | @interface PSPDFViewController (Internal) 17 | - (void)delegateDidShowController:(id)viewController embeddedInController:(id)controller options:(NSDictionary *)options animated:(BOOL)animated; 18 | @end 19 | 20 | @implementation TIPSPDFViewController 21 | 22 | /////////////////////////////////////////////////////////////////////////////////////////////////// 23 | #pragma mark - Lifecycle 24 | 25 | - (void)dealloc { 26 | PSTiLog(@"dealloc: %@", self) 27 | self.proxy = nil; // forget proxy 28 | } 29 | 30 | /////////////////////////////////////////////////////////////////////////////////////////////////// 31 | #pragma mark - Public 32 | 33 | - (void)setProxy:(TIPSPDFViewControllerProxy *)proxy { 34 | if (proxy != _proxy) { 35 | [_proxy forgetSelf]; 36 | _proxy = proxy; 37 | [proxy rememberSelf]; 38 | } 39 | } 40 | 41 | - (void)closeControllerAnimated:(BOOL)animated { 42 | PSCLog(@"closing controller animated: %d", animated); 43 | [self dismissViewControllerAnimated:animated completion:NULL]; 44 | } 45 | 46 | /////////////////////////////////////////////////////////////////////////////////////////////////// 47 | #pragma mark - UIViewController 48 | 49 | - (void)viewWillDisappear:(BOOL)animated { 50 | [super viewWillDisappear:animated]; 51 | 52 | if (self.navigationController.isBeingDismissed) { 53 | [self.proxy fireEvent:@"willCloseController" withObject:nil]; 54 | } 55 | } 56 | 57 | - (void)viewDidDisappear:(BOOL)animated { 58 | [super viewDidDisappear:animated]; 59 | 60 | if (self.navigationController.isBeingDismissed) { 61 | [self.proxy fireEvent:@"didCloseController" withObject:nil]; 62 | self.proxy = nil; 63 | } 64 | } 65 | 66 | // If we return YES here, UIWindow leaks our controller in the Titanium configuration. 67 | - (BOOL)canBecomeFirstResponder { 68 | return NO; 69 | } 70 | 71 | /////////////////////////////////////////////////////////////////////////////////////////// 72 | #pragma mark - PSPDFViewController 73 | 74 | - (void)delegateDidShowController:(id)viewController embeddedInController:(id)controller options:(NSDictionary *)options animated:(BOOL)animated { 75 | [super delegateDidShowController:viewController embeddedInController:controller options:options animated:animated]; 76 | 77 | // Fire event when a popover is displayed. 78 | [self.proxy fireEvent:@"didPresentPopover" withObject:viewController]; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /Classes/TIPSPDFViewControllerProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | @class TIPSPDFViewController, ComPspdfkitModule, ComPspdfkitViewProxy; 14 | 15 | /// Appcelerator Proxy for PSPDFViewController. Exposes a subset. 16 | @interface TIPSPDFViewControllerProxy : TiProxy 17 | 18 | @property (atomic, weak) TIPSPDFViewController *controller; // we're keeping a reverse relation with associated object 19 | 20 | @property (atomic, weak) ComPspdfkitViewProxy *viewProxy; 21 | 22 | // proxy is initialized and keeps the view controller around 23 | - (id)initWithPDFController:(TIPSPDFViewController *)pdfController context:(id)context parentProxy:(TiProxy *)parentProxy; 24 | 25 | /// Returns the documentPath. 26 | - (id)documentPath; 27 | 28 | /// scroll to a specific page. Argument 1 = integer, argument 2 = animated. (optional, defaults to YES) 29 | - (void)scrollToPage:(id)args; 30 | 31 | /// change view mode argument 1 = integer, argument 2 = animated. (optional, defaults to YES) 32 | - (void)setViewMode:(id)args; 33 | 34 | /// Opens the PSPDFSearchViewController with the searchString. 35 | - (void)searchForString:(id)args; 36 | 37 | /// Current page. 38 | - (id)page; 39 | 40 | /// Returns total pages count. 41 | - (id)totalPages; 42 | 43 | /// close controller. (argument 1 = animated) 44 | - (void)close:(id)args; 45 | 46 | /// Register a callback for the didTapOnAnnotation event. Return true if you manually use the annotation, else false. 47 | - (void)setDidTapOnAnnotationCallback:(KrollCallback *)callback; 48 | 49 | /// Opens the PSPDFOutlineViewController 50 | - (void)showOutlineView:(id)arg; 51 | 52 | /// Opens the PSPDFSearchViewController 53 | - (void)showSearchView:(id)arg; 54 | 55 | /// Opens the PSPDFBrightnessViewController 56 | - (void)showBrightnessView:(id)arg; 57 | 58 | /// Opens the UIPrintInteractionController 59 | - (void)showPrintView:(id)arg; 60 | 61 | /// Opens the MFMailComposeViewController 62 | - (void)showEmailView:(id)arg; 63 | 64 | /// Opens the PSPDFAnnotationToolbar 65 | - (void)showAnnotationView:(id)arg; 66 | 67 | /// Opens the UIDocumentInteractionController 68 | - (void)showOpenInView:(id)arg; 69 | 70 | /// Opens the UIActivityViewController 71 | - (void)showActivityView:(id)arg; 72 | 73 | /// Bookmark the current page 74 | - (void)bookmarkPage:(id)arg; 75 | 76 | /// Exposes a helper to change link annotation stroke width. Set to change. 77 | - (void)setLinkAnnotationStrokeWidth:(id)arg; 78 | 79 | /// Exposes a helper to change link annotation color. Set to change. 80 | - (void)setLinkAnnotationBorderColor:(id)arg; 81 | 82 | /// Exposes a helper to change link annotation highlight color. Set to change. 83 | - (void)setLinkAnnotationHighlightColor:(id)arg; 84 | 85 | /// Set list of editable annotation types. 86 | - (void)setEditableAnnotationTypes:(id)arg; 87 | 88 | /// Exposes helper to set `thumbnailController.filterOptions`. 89 | - (void)setThumbnailFilterOptions:(id)arg; 90 | 91 | /// Exposes helper to set `outlineBarButtonItem.availableControllerOptions` 92 | - (void)setOutlineControllerFilterOptions:(id)arg; 93 | 94 | /// Document's menu actions. 95 | - (void)setAllowedMenuActions:(id)arg; 96 | 97 | /// Expose the scrollEnabled property 98 | - (void)setScrollEnabled:(id)args; 99 | 100 | // Save changed annotations. 101 | - (void)saveAnnotations:(id)args; 102 | 103 | // PSPDFDocument's annotationSaveMode property. 104 | - (void)setAnnotationSaveMode:(id)arg; 105 | 106 | // Hide any visible popover. arg: animated YES/NO 107 | - (void)hidePopover:(id)args; 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /Classes/TIPSPDFViewControllerProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | #import "TIPSPDFViewControllerProxy.h" 11 | 12 | #import "ComPspdfkitModule.h" 13 | #import "ComPspdfkitViewProxy.h" 14 | #import "PSPDFUtils.h" 15 | #import "TIPSPDFViewController.h" 16 | #import 17 | 18 | #define PSC_SILENCE_CALL_TO_UNKNOWN_SELECTOR(expression) \ 19 | _Pragma("clang diagnostic push") \ 20 | _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ 21 | expression \ 22 | _Pragma("clang diagnostic pop") 23 | 24 | #define PSCWeakifyAs(object, weakName) typeof(object) __weak weakName = object 25 | 26 | void (^tipspdf_targetActionBlock(id target, SEL action))(id) { 27 | // If there's no target, return an empty block. 28 | if (!target) return ^(__unused id sender) {}; 29 | 30 | NSCParameterAssert(action); 31 | 32 | // All ObjC methods have two arguments. This fails if either target is nil, action not implemented or else. 33 | NSUInteger numberOfArguments = [target methodSignatureForSelector:action].numberOfArguments; 34 | NSCAssert(numberOfArguments == 2 || numberOfArguments == 3, @"%@ should have at most one argument.", NSStringFromSelector(action)); 35 | 36 | PSCWeakifyAs(target, weakTarget); 37 | if (numberOfArguments == 2) { 38 | return ^(__unused id sender) { PSC_SILENCE_CALL_TO_UNKNOWN_SELECTOR([weakTarget performSelector:action];) }; 39 | } else { 40 | return ^(id sender) { PSC_SILENCE_CALL_TO_UNKNOWN_SELECTOR([weakTarget performSelector:action withObject:sender];) }; 41 | } 42 | } 43 | 44 | @interface TIPSPDFViewControllerProxy () 45 | 46 | @property (nonatomic) KrollCallback *didTapOnAnnotationCallback; 47 | @property (nonatomic, weak) TiProxy *parentProxy; 48 | @property (atomic) CGFloat linkAnnotationBackedStrokeWidth; 49 | @property (atomic) UIColor *linkAnnotationBorderBackedColor; 50 | @property (atomic) UIColor *linkAnnotationHighlightBackedColor; 51 | 52 | @end 53 | 54 | @implementation TIPSPDFViewControllerProxy 55 | 56 | /////////////////////////////////////////////////////////////////////////////////////////////////// 57 | #pragma mark - Lifecycle 58 | 59 | - (id)initWithPDFController:(TIPSPDFViewController *)pdfController context:(id)context parentProxy:(TiProxy *)parentProxy { 60 | if ((self = [super _initWithPageContext:context])) { 61 | PSTiLog(@"init TIPSPDFViewControllerProxy"); 62 | self.parentProxy = parentProxy; 63 | self.controller = pdfController; 64 | self.controller.delegate = self; 65 | // As long as pdfController exists, we're not getting released. 66 | pdfController.proxy = self; 67 | self.modelDelegate = self; 68 | } 69 | return self; 70 | } 71 | 72 | - (void)dealloc { 73 | PSTiLog(@"Deallocating proxy %@", self); 74 | } 75 | 76 | - (TiProxy *)parentForBubbling { 77 | return self.parentProxy; 78 | } 79 | 80 | /////////////////////////////////////////////////////////////////////////////////////////////////// 81 | #pragma mark - Public 82 | 83 | - (id)page { 84 | __block NSUInteger page = 0; 85 | if (![NSThread isMainThread]) { 86 | dispatch_sync(dispatch_get_main_queue(), ^{ 87 | page = [[self page] unsignedIntegerValue]; 88 | }); 89 | }else { 90 | page = self.controller.pageIndex; 91 | } 92 | 93 | return @(page); 94 | } 95 | 96 | - (id)totalPages { 97 | __block NSUInteger totalPages = 0; 98 | if (![NSThread isMainThread]) { 99 | dispatch_sync(dispatch_get_main_queue(), ^{ 100 | totalPages = [[self totalPages] unsignedIntegerValue]; 101 | }); 102 | }else { 103 | totalPages = [@([_controller.document pageCount]) unsignedIntegerValue]; 104 | } 105 | 106 | return @(totalPages); 107 | } 108 | 109 | - (id)documentPath { 110 | return [[self.controller.document fileURL] path]; 111 | } 112 | 113 | - (void)setLinkAnnotationStrokeWidth:(id)arg { 114 | ENSURE_UI_THREAD(setLinkAnnotationStrokeWidth, arg); 115 | 116 | self.linkAnnotationBackedStrokeWidth = [PSPDFUtils floatValue:arg]; 117 | // Ensure controller is reloaded. 118 | [self.controller reloadData]; 119 | } 120 | 121 | - (void)setLinkAnnotationBorderColor:(id)arg { 122 | ENSURE_UI_THREAD(setLinkAnnotationBorderColor, arg); 123 | 124 | self.linkAnnotationBorderBackedColor = [PSPDFUtils colorFromArg:arg]; 125 | // Ensure controller is reloaded. 126 | [self.controller reloadData]; 127 | } 128 | 129 | - (void)setLinkAnnotationHighlightColor:(id)arg { 130 | ENSURE_UI_THREAD(setLinkAnnotationHighlightColor, arg); 131 | 132 | self.linkAnnotationHighlightBackedColor = [PSPDFUtils colorFromArg:arg]; 133 | // Ensure controller is reloaded. 134 | [self.controller reloadData]; 135 | } 136 | 137 | - (void)setEditableAnnotationTypes:(id)arg { 138 | ENSURE_UI_THREAD(setEditableAnnotationTypes, arg); 139 | 140 | NSMutableSet *editableAnnotationTypes = [NSMutableSet set]; 141 | if ([arg isKindOfClass:NSArray.class]) { 142 | for (__strong NSString *item in arg) { 143 | item = PSSafeCast(item, NSString.class); 144 | if (PSPDFAnnotationTypeFromString(item) > 0) { 145 | [editableAnnotationTypes addObject:item]; 146 | } 147 | } 148 | } 149 | 150 | [self.controller updateConfigurationWithBuilder:^(PSPDFConfigurationBuilder *builder) { 151 | builder.editableAnnotationTypes = editableAnnotationTypes; 152 | }]; 153 | } 154 | 155 | - (void)setThumbnailFilterOptions:(id)arg { 156 | ENSURE_UI_THREAD(setThumbnailFilterOptions, arg); 157 | 158 | NSMutableArray *filterOptions = [NSMutableArray array]; 159 | if ([arg isKindOfClass:NSArray.class]) { 160 | for (__strong NSString *filter in arg) { 161 | filter = [PSSafeCast(filter, NSString.class) lowercaseString]; 162 | if ([filter isEqual:@"all"]) { 163 | [filterOptions addObject:PSPDFThumbnailViewFilterShowAll]; 164 | }else if ([filter isEqual:@"bookmarks"]) { 165 | [filterOptions addObject:PSPDFThumbnailViewFilterBookmarks]; 166 | }else if ([filter isEqual:@"annotations"]) { 167 | [filterOptions addObject:PSPDFThumbnailViewFilterAnnotations]; 168 | } 169 | } 170 | } 171 | self.controller.thumbnailController.filterOptions = filterOptions; 172 | } 173 | 174 | - (void)setOutlineControllerFilterOptions:(id)arg { 175 | ENSURE_UI_THREAD(setOutlineControllerFilterOptions, arg); 176 | 177 | NSMutableArray *filterOptions = [NSMutableArray array]; 178 | if ([arg isKindOfClass:NSArray.class]) { 179 | for (__strong NSString *filter in arg) { 180 | filter = [PSSafeCast(filter, NSString.class) lowercaseString]; 181 | if ([filter isEqual:@"outline"]) { 182 | [filterOptions addObject:PSPDFDocumentInfoOptionOutline]; 183 | } else if ([filter isEqual:@"bookmarks"]) { 184 | [filterOptions addObject:PSPDFDocumentInfoOptionBookmarks]; 185 | } else if ([filter isEqual:@"annotations"]) { 186 | [filterOptions addObject:PSPDFDocumentInfoOptionAnnotations]; 187 | } else if ([filter isEqual:@"files"]) { 188 | [filterOptions addObject:PSPDFDocumentInfoOptionEmbeddedFiles]; 189 | } 190 | } 191 | } 192 | self.controller.documentInfoCoordinator.availableControllerOptions = filterOptions; 193 | } 194 | 195 | - (void)setAllowedMenuActions:(id)arg { 196 | ENSURE_UI_THREAD(setAllowedMenuActions, arg); 197 | 198 | NSUInteger menuActions = 0; 199 | if ([arg isKindOfClass:NSArray.class]) { 200 | for (__strong NSString *filter in arg) { 201 | filter = [PSSafeCast(filter, NSString.class) lowercaseString]; 202 | if ([filter isEqual:@"search"]) { 203 | menuActions |= PSPDFTextSelectionMenuActionSearch; 204 | }else if ([filter isEqual:@"define"]) { 205 | menuActions |= PSPDFTextSelectionMenuActionDefine; 206 | }else if ([filter isEqual:@"wikipedia"]) { 207 | menuActions |= PSPDFTextSelectionMenuActionWikipedia; 208 | } 209 | } 210 | } 211 | if (menuActions > 0) { 212 | [self.controller updateConfigurationWithBuilder:^(PSPDFConfigurationBuilder *builder) { 213 | builder.allowedMenuActions = (PSPDFTextSelectionMenuAction) menuActions; 214 | }]; 215 | } 216 | } 217 | 218 | - (void)setScrollEnabled:(id)args { 219 | ENSURE_UI_THREAD(setScrollEnabled, args); 220 | 221 | NSUInteger pageValue = [PSPDFUtils intValue:args onPosition:0]; 222 | [_controller.documentViewController setScrollEnabled:pageValue]; 223 | } 224 | 225 | - (void)scrollToPage:(id)args { 226 | ENSURE_UI_THREAD(scrollToPage, args); 227 | 228 | PSCLog(@"scrollToPage: %@", args); 229 | NSUInteger pageValue = [PSPDFUtils intValue:args onPosition:0]; 230 | NSUInteger animationValue = [PSPDFUtils intValue:args onPosition:1]; 231 | BOOL animated = animationValue == NSNotFound || animationValue == 1; 232 | [self.controller setPageIndex:pageValue animated:animated]; 233 | } 234 | 235 | - (void)setViewMode:(id)args { 236 | ENSURE_UI_THREAD(setViewMode, args); 237 | 238 | PSCLog(@"setViewMode: %@", args); 239 | NSUInteger viewModeValue = [PSPDFUtils intValue:args onPosition:0]; 240 | NSUInteger animationValue = [PSPDFUtils intValue:args onPosition:1]; 241 | BOOL animated = animationValue == NSNotFound || animationValue == 1; 242 | [self.controller setViewMode:viewModeValue animated:animated]; 243 | } 244 | 245 | - (void)searchForString:(id)args { 246 | ENSURE_UI_THREAD(searchForString, args); 247 | 248 | if (![args isKindOfClass:NSArray.class] || [args count] < 1 || ![args[0] isKindOfClass:NSString.class]) { 249 | PSCLog(@"Argument error, expected 1-2 arguments: %@", args); 250 | return; 251 | } 252 | 253 | PSCLog(@"searchForString: %@", args); 254 | NSString *searchString = args[0]; 255 | BOOL animated = [PSPDFUtils intValue:args onPosition:1] > 0; 256 | [self.controller searchForString:searchString options:nil sender:nil animated:animated]; 257 | } 258 | 259 | - (void)close:(id)args { 260 | ENSURE_UI_THREAD(close, args); 261 | 262 | PSCLog(@"Closing controller: %@", _controller); 263 | NSUInteger animationValue = [PSPDFUtils intValue:args onPosition:1]; 264 | BOOL animated = animationValue == NSNotFound || animationValue == 1; 265 | 266 | [self.controller closeControllerAnimated:animated]; 267 | } 268 | 269 | - (void)setDidTapOnAnnotationCallback:(KrollCallback *)callback { 270 | if (![callback isKindOfClass:[KrollCallback class]]) { 271 | [self throwException:TiExceptionInvalidType subreason:[NSString stringWithFormat:@"expected: %@, was: %@",CLASS2JS([KrollCallback class]),OBJTYPE2JS(callback)] location:CODELOCATION]; 272 | } 273 | 274 | PSCLog(@"registering annotation callback: %@", callback); 275 | if (self.didTapOnAnnotationCallback != callback) { 276 | // Use ivar to prevent infinite loop 277 | _didTapOnAnnotationCallback = callback; 278 | } 279 | } 280 | 281 | - (void)saveAnnotations:(id)args { 282 | ENSURE_UI_THREAD(saveAnnotations, args); 283 | 284 | NSError *error = nil; 285 | BOOL success = [self.controller.document saveWithOptions:nil error:&error]; 286 | if (!success && self.controller.configuration.isTextSelectionEnabled) { 287 | PSCLog(@"Saving annotations failed: %@", [error localizedDescription]); 288 | } 289 | [[self eventProxy] fireEvent:@"didSaveAnnotations" withObject:@{@"success" : @(success)}]; 290 | } 291 | 292 | - (void)setAnnotationSaveMode:(id)arg { 293 | ENSURE_SINGLE_ARG(arg, NSNumber); 294 | ENSURE_UI_THREAD(setAnnotationSaveMode, arg); 295 | 296 | PSPDFAnnotationSaveMode annotationSaveMode = [arg integerValue]; 297 | self.controller.document.annotationSaveMode = annotationSaveMode; 298 | } 299 | 300 | - (void)hidePopover:(id)args { 301 | ENSURE_UI_THREAD(hidePopover, args); 302 | 303 | BOOL const animated = [args count] == 1 && [args[0] boolValue]; 304 | [self.controller.presentedViewController dismissViewControllerAnimated:animated completion:NULL]; 305 | } 306 | 307 | #define PSPDF_SILENCE_CALL_TO_UNKNOWN_SELECTOR(expression) \ 308 | _Pragma("clang diagnostic push") \ 309 | _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ 310 | expression \ 311 | _Pragma("clang diagnostic pop") 312 | 313 | - (void)showBarButton:(SEL)barButtonSEL action:(id)action { 314 | dispatch_async(dispatch_get_main_queue(), ^{ 315 | PSPDF_SILENCE_CALL_TO_UNKNOWN_SELECTOR(UIBarButtonItem *barButtonItem = [self.controller performSelector:barButtonSEL];) 316 | id const sender = action ? [action[0] view] : self; 317 | tipspdf_targetActionBlock(barButtonItem.target, barButtonItem.action)(sender); 318 | }); 319 | } 320 | 321 | - (void)showOutlineView:(id)arg { 322 | [self showBarButton:@selector(outlineButtonItem) action:arg]; 323 | } 324 | 325 | - (void)showSearchView:(id)arg { 326 | [self showBarButton:@selector(searchButtonItem) action:arg]; 327 | } 328 | 329 | - (void)showBrightnessView:(id)arg { 330 | [self showBarButton:@selector(brightnessButtonItem) action:arg]; 331 | } 332 | 333 | - (void)showPrintView:(id)arg { 334 | [self showBarButton:@selector(printButtonItem) action:arg]; 335 | } 336 | 337 | - (void)showEmailView:(id)arg { 338 | [self showBarButton:@selector(emailButtonItem) action:arg]; 339 | } 340 | 341 | - (void)showAnnotationView:(id)arg { 342 | [self showBarButton:@selector(annotationButtonItem) action:arg]; 343 | } 344 | 345 | - (void)showOpenInView:(id)arg { 346 | [self showBarButton:@selector(openInButtonItem) action:arg]; 347 | } 348 | 349 | - (void)showActivityView:(id)arg { 350 | [self showBarButton:@selector(activityButtonItem) action:arg]; 351 | } 352 | 353 | - (void)bookmarkPage:(id)arg { 354 | ENSURE_UI_THREAD(bookmarkPage, arg); 355 | 356 | UIBarButtonItem *bookmarkButtonItem = self.controller.bookmarkButtonItem; 357 | pst_targetActionBlock(bookmarkButtonItem.target, bookmarkButtonItem.action)(bookmarkButtonItem); 358 | } 359 | 360 | /////////////////////////////////////////////////////////////////////////////////////////////////// 361 | #pragma mark - TiProxyDelegate 362 | 363 | - (void)propertyChanged:(NSString*)key oldValue:(id)oldValue newValue:(id)newValue proxy:(TiProxy *)proxy { 364 | PSCLog(@"Received property change: %@ -> %@", key, newValue); 365 | 366 | // PSPDFViewController is *not* thread safe. only set on main thread. 367 | ps_dispatch_main_async(^{ 368 | [PSPDFUtils applyOptions:@{key: newValue} onObject:self.controller]; 369 | }); 370 | } 371 | 372 | /////////////////////////////////////////////////////////////////////////////////////////////////// 373 | #pragma mark - PSPDFViewControllerDelegate 374 | 375 | - (TiProxy *)eventProxy { 376 | ComPspdfkitViewProxy *viewProxy = [self viewProxy]; 377 | PSTiLog(@"viewProxy_: %@ self: %@", viewProxy, self); 378 | TiProxy *eventProxy = viewProxy ?: self; 379 | PSTiLog(@"eventProxy: %@: %@", viewProxy ? @"view" : @"self", eventProxy) 380 | return eventProxy; 381 | } 382 | 383 | /// delegate for tapping on an annotation. If you don't implement this or return false, it will be processed by default action (scroll to page, ask to open Safari) 384 | - (BOOL)pdfViewController:(PSPDFViewController *)pdfController didTapOnAnnotation:(PSPDFAnnotation *)annotation annotationPoint:(CGPoint)annotationPoint annotationView:(UIView *)annotationView pageView:(PSPDFPageView *)pageView viewPoint:(CGPoint)viewPoint { 385 | NSParameterAssert([pdfController isKindOfClass:[TIPSPDFViewController class]]); 386 | 387 | NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:@(pageView.pageIndex), @"page", nil]; 388 | // only set a subset 389 | if ([annotation isKindOfClass:[PSPDFLinkAnnotation class]]) { 390 | PSPDFLinkAnnotation *linkAnnotation = (PSPDFLinkAnnotation *)annotation; 391 | 392 | if (linkAnnotation.URL) { 393 | eventDict[@"URL"] = linkAnnotation.URL; 394 | eventDict[@"siteLinkTarget"] = linkAnnotation.URL.absoluteString; 395 | }else if (linkAnnotation.linkType == PSPDFLinkAnnotationPage) { 396 | // We don't forward all proxy types. 397 | if ([linkAnnotation.action respondsToSelector:@selector(pageIndex)]) { 398 | eventDict[@"pageIndex"] = @(((PSPDFGoToAction *)linkAnnotation.action).pageIndex); 399 | eventDict[@"pageLinkTarget"] = @(((PSPDFGoToAction *)linkAnnotation.action).pageIndex); // Deprecated 400 | } 401 | } 402 | 403 | PSPDFAction *action = linkAnnotation.action; 404 | if (action) { 405 | if (action.type == PSPDFActionTypeRemoteGoTo) { 406 | eventDict[@"relativePath"] = ((PSPDFRemoteGoToAction *)action).relativePath; 407 | eventDict[@"pageIndex"] = @(((PSPDFRemoteGoToAction *)action).pageIndex); 408 | } 409 | 410 | // Translate the type. 411 | id actionTypeString = [[NSValueTransformer valueTransformerForName:PSPDFActionTypeTransformerName] reverseTransformedValue:@(action.type)]; 412 | if (actionTypeString) eventDict[@"actionType"] = actionTypeString; 413 | } 414 | } 415 | 416 | BOOL processed = NO; 417 | if(self.didTapOnAnnotationCallback) { 418 | id retVal = [self.didTapOnAnnotationCallback call:@[eventDict] thisObject:nil]; 419 | processed = [retVal boolValue]; 420 | PSCLog(@"retVal: %d", processed); 421 | } 422 | 423 | if ([[self eventProxy] _hasListeners:@"didTapOnAnnotation"]) { 424 | [[self eventProxy] fireEvent:@"didTapOnAnnotation" withObject:eventDict]; 425 | } 426 | return processed; 427 | } 428 | 429 | /// controller did begin displaying a new page (at least 51% of it is visible) 430 | - (void)pdfViewController:(PSPDFViewController *)pdfController willBeginDisplayingPageView:(PSPDFPageView *)pageView forPageAtIndex:(NSInteger)pageIndex { 431 | if ([[self eventProxy] _hasListeners:@"willBeginDisplayingPageView"]) { 432 | NSDictionary *eventDict = @{@"page": @(pageIndex)}; 433 | [[self eventProxy] fireEvent:@"willBeginDisplayingPageView" withObject:eventDict]; 434 | } 435 | } 436 | 437 | /// page was fully rendered 438 | - (void)pdfViewController:(PSPDFViewController *)pdfController didFinishRenderTaskForPageView:(PSPDFPageView *)pageView { 439 | if ([[self eventProxy] _hasListeners:@"didFinishRenderTaskForPageView"]) { 440 | NSDictionary *eventDict = @{@"page": @(pageView.pageIndex)}; 441 | [[self eventProxy] fireEvent:@"didFinishRenderTaskForPageView" withObject:eventDict]; 442 | } 443 | } 444 | 445 | /// will be called when viewMode changes 446 | - (void)pdfViewController:(PSPDFViewController *)pdfController didChangeViewMode:(PSPDFViewMode)viewMode { 447 | if ([[self eventProxy] _hasListeners:@"didChangeViewMode"]) { 448 | NSDictionary *eventDict = @{@"viewMode": @(viewMode)}; 449 | [[self eventProxy] fireEvent:@"didChangeViewMode" withObject:eventDict]; 450 | } 451 | } 452 | 453 | - (UIView *)pdfViewController:(PSPDFViewController *)pdfController annotationView:(UIView *)annotationView forAnnotation:(PSPDFAnnotation *)annotation onPageView:(PSPDFPageView *)pageView { 454 | if (annotation.type == PSPDFAnnotationTypeLink && [annotationView isKindOfClass:[PSPDFLinkAnnotationView class]]) { 455 | PSPDFLinkAnnotationView *linkAnnotation = (PSPDFLinkAnnotationView *)annotationView; 456 | if (self.linkAnnotationBorderBackedColor) { 457 | linkAnnotation.borderColor = self.linkAnnotationBorderBackedColor; 458 | } 459 | if (self.linkAnnotationBackedStrokeWidth) { 460 | linkAnnotation.strokeWidth = self.linkAnnotationBackedStrokeWidth; 461 | } 462 | } 463 | return annotationView; 464 | } 465 | 466 | - (BOOL)pdfViewController:(PSPDFViewController *)pdfController shouldShowUserInterface:(BOOL)animated { 467 | if ([[self eventProxy] _hasListeners:@"shouldShowUserInterface"]) { 468 | [[self eventProxy] fireEvent:@"shouldShowUserInterface" withObject:nil]; 469 | } 470 | if ([[self eventProxy] _hasListeners:@"willShowUserInterface"]) { 471 | [[self eventProxy] fireEvent:@"willShowUserInterface" withObject:nil]; 472 | } 473 | return YES; 474 | } 475 | 476 | - (void)pdfViewController:(PSPDFViewController *)pdfController didShowUserInterface:(BOOL)animated { 477 | if ([[self eventProxy] _hasListeners:@"didShowUserInterface"]) { 478 | [[self eventProxy] fireEvent:@"didShowUserInterface" withObject:nil]; 479 | } 480 | } 481 | 482 | - (BOOL)pdfViewController:(PSPDFViewController *)pdfController shouldHideUserInterface:(BOOL)animated { 483 | if ([[self eventProxy] _hasListeners:@"shouldHideUserInterface"]) { 484 | [[self eventProxy] fireEvent:@"shouldHideUserInterface" withObject:nil]; 485 | } 486 | if ([[self eventProxy] _hasListeners:@"willHideUserInterface"]) { 487 | [[self eventProxy] fireEvent:@"willHideUserInterface" withObject:nil]; 488 | } 489 | return YES; 490 | } 491 | 492 | - (void)pdfViewController:(PSPDFViewController *)pdfController didHideUserInterface:(BOOL)animated { 493 | if ([[self eventProxy] _hasListeners:@"didHideUserInterface"]) { 494 | [[self eventProxy] fireEvent:@"didHideUserInterface" withObject:nil]; 495 | } 496 | } 497 | 498 | @end 499 | 500 | id PSSafeCast(id object, Class targetClass) { 501 | NSCParameterAssert(targetClass); 502 | return [object isKindOfClass:targetClass] ? object : nil; 503 | } 504 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This wrapper is licensed with a modified BSD license. 2 | 3 | In plain language: you're allowed to do whatever you wish with 4 | the code, modify, redistribute, embed in your products (free or 5 | commercial), but you must include copyright, terms of usage and 6 | disclaimer as stated in the license. 7 | 8 | You will require a commercial PSPDFKit License to run these examples 9 | in non-demo mode. Please refer to sales@pspdfkit.com for details. 10 | 11 | Copyright (c) 2010-2021 PSPDFKit GmbH. All rights reserved. 12 | 13 | Redistribution and use in source or binary forms, 14 | with or without modification, are permitted provided 15 | that the following conditions are met: 16 | 17 | - Redistributions of source code must retain the above copyright 18 | notice, this list of conditions and the following disclaimer. 19 | 20 | - Redistributions in binary form must reproduce the above copyright 21 | notice, this list of conditions and the following disclaimer in the 22 | documentation and/or other materials provided with the 23 | distribution. 24 | 25 | - Redistributions of PSPDFKit Samples must include attribution to 26 | PSPDFKit, either in documentation or other appropriate media. 27 | 28 | - Neither the name of the PSPDFKit, PSPDFKit GmbH, nor its developers 29 | may be used to endorse or promote products derived from 30 | this software without specific prior written permission. 31 | 32 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 33 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 34 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 35 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 36 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 37 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 38 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 39 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 40 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 41 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 42 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 | -------------------------------------------------------------------------------- /PSPDFKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3ACB40052541D527000561FC /* PSPDFKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3ACB3FF92541C690000561FC /* PSPDFKit.xcframework */; }; 11 | 3ACB40072541D52C000561FC /* PSPDFKitUI.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3ACB3FFA2541C691000561FC /* PSPDFKitUI.xcframework */; }; 12 | 3AFF6A032465F159004587CE /* TIPSPDFAnnotationProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AFF69F22465F159004587CE /* TIPSPDFAnnotationProxy.h */; }; 13 | 3AFF6A042465F159004587CE /* PSPDFUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AFF69F32465F159004587CE /* PSPDFUtils.m */; }; 14 | 3AFF6A052465F159004587CE /* ComPspdfkitViewProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AFF69F42465F159004587CE /* ComPspdfkitViewProxy.m */; }; 15 | 3AFF6A062465F159004587CE /* ComPspdfkitModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AFF69F52465F159004587CE /* ComPspdfkitModule.m */; }; 16 | 3AFF6A072465F159004587CE /* ComPspdfkitViewProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AFF69F62465F159004587CE /* ComPspdfkitViewProxy.h */; }; 17 | 3AFF6A082465F159004587CE /* TIPSPDFAnnotationProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AFF69F72465F159004587CE /* TIPSPDFAnnotationProxy.m */; }; 18 | 3AFF6A092465F159004587CE /* TIPSPDFViewControllerProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AFF69F82465F159004587CE /* TIPSPDFViewControllerProxy.h */; }; 19 | 3AFF6A0A2465F159004587CE /* ComPspdfkitView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AFF69F92465F159004587CE /* ComPspdfkitView.m */; }; 20 | 3AFF6A0B2465F159004587CE /* TIPSPDFViewControllerProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AFF69FA2465F159004587CE /* TIPSPDFViewControllerProxy.m */; }; 21 | 3AFF6A0C2465F159004587CE /* ComPspdfkitPrefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 3AFF69FB2465F159004587CE /* ComPspdfkitPrefix.pch */; }; 22 | 3AFF6A0D2465F159004587CE /* ComPspdfkitModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AFF69FC2465F159004587CE /* ComPspdfkitModule.h */; }; 23 | 3AFF6A0E2465F159004587CE /* TIPSPDFViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AFF69FD2465F159004587CE /* TIPSPDFViewController.m */; }; 24 | 3AFF6A0F2465F159004587CE /* ComPspdfkitModuleAssets.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AFF69FE2465F159004587CE /* ComPspdfkitModuleAssets.h */; }; 25 | 3AFF6A102465F159004587CE /* TIPSPDFViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AFF69FF2465F159004587CE /* TIPSPDFViewController.h */; }; 26 | 3AFF6A112465F159004587CE /* ComPspdfkitModuleAssets.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AFF6A002465F159004587CE /* ComPspdfkitModuleAssets.m */; }; 27 | 3AFF6A122465F159004587CE /* PSPDFUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AFF6A012465F159004587CE /* PSPDFUtils.h */; }; 28 | 3AFF6A132465F159004587CE /* ComPspdfkitView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AFF6A022465F159004587CE /* ComPspdfkitView.h */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 24DD6D1B1134B66800162E58 /* titanium.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = titanium.xcconfig; sourceTree = ""; }; 33 | 3ACB3FF92541C690000561FC /* PSPDFKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PSPDFKit.xcframework; path = platform/PSPDFKit.xcframework; sourceTree = SOURCE_ROOT; }; 34 | 3ACB3FFA2541C691000561FC /* PSPDFKitUI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PSPDFKitUI.xcframework; path = platform/PSPDFKitUI.xcframework; sourceTree = SOURCE_ROOT; }; 35 | 3AFF69F22465F159004587CE /* TIPSPDFAnnotationProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TIPSPDFAnnotationProxy.h; sourceTree = ""; }; 36 | 3AFF69F32465F159004587CE /* PSPDFUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSPDFUtils.m; sourceTree = ""; }; 37 | 3AFF69F42465F159004587CE /* ComPspdfkitViewProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ComPspdfkitViewProxy.m; sourceTree = ""; }; 38 | 3AFF69F52465F159004587CE /* ComPspdfkitModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ComPspdfkitModule.m; sourceTree = ""; }; 39 | 3AFF69F62465F159004587CE /* ComPspdfkitViewProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComPspdfkitViewProxy.h; sourceTree = ""; }; 40 | 3AFF69F72465F159004587CE /* TIPSPDFAnnotationProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TIPSPDFAnnotationProxy.m; sourceTree = ""; }; 41 | 3AFF69F82465F159004587CE /* TIPSPDFViewControllerProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TIPSPDFViewControllerProxy.h; sourceTree = ""; }; 42 | 3AFF69F92465F159004587CE /* ComPspdfkitView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ComPspdfkitView.m; sourceTree = ""; }; 43 | 3AFF69FA2465F159004587CE /* TIPSPDFViewControllerProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TIPSPDFViewControllerProxy.m; sourceTree = ""; }; 44 | 3AFF69FB2465F159004587CE /* ComPspdfkitPrefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComPspdfkitPrefix.pch; sourceTree = ""; }; 45 | 3AFF69FC2465F159004587CE /* ComPspdfkitModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComPspdfkitModule.h; sourceTree = ""; }; 46 | 3AFF69FD2465F159004587CE /* TIPSPDFViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TIPSPDFViewController.m; sourceTree = ""; }; 47 | 3AFF69FE2465F159004587CE /* ComPspdfkitModuleAssets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComPspdfkitModuleAssets.h; sourceTree = ""; }; 48 | 3AFF69FF2465F159004587CE /* TIPSPDFViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TIPSPDFViewController.h; sourceTree = ""; }; 49 | 3AFF6A002465F159004587CE /* ComPspdfkitModuleAssets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ComPspdfkitModuleAssets.m; sourceTree = ""; }; 50 | 3AFF6A012465F159004587CE /* PSPDFUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSPDFUtils.h; sourceTree = ""; }; 51 | 3AFF6A022465F159004587CE /* ComPspdfkitView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComPspdfkitView.h; sourceTree = ""; }; 52 | D2AAC07E0554694100DB518D /* libComPspdfkit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libComPspdfkit.a; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | D2AAC07C0554694100DB518D /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 3ACB40072541D52C000561FC /* PSPDFKitUI.xcframework in Frameworks */, 61 | 3ACB40052541D527000561FC /* PSPDFKit.xcframework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | 034768DFFF38A50411DB9C8B /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | D2AAC07E0554694100DB518D /* libComPspdfkit.a */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 0867D691FE84028FC02AAC07 /* pspdfkit */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 08FB77AEFE84172EC02AAC07 /* Classes */, 80 | 32C88DFF0371C24200C91783 /* Other Sources */, 81 | 3ACB3FF82541C690000561FC /* Frameworks */, 82 | 034768DFFF38A50411DB9C8B /* Products */, 83 | ); 84 | indentWidth = 4; 85 | name = pspdfkit; 86 | sourceTree = ""; 87 | tabWidth = 4; 88 | usesTabs = 0; 89 | }; 90 | 08FB77AEFE84172EC02AAC07 /* Classes */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 3AFF69FC2465F159004587CE /* ComPspdfkitModule.h */, 94 | 3AFF69F52465F159004587CE /* ComPspdfkitModule.m */, 95 | 3AFF69FE2465F159004587CE /* ComPspdfkitModuleAssets.h */, 96 | 3AFF6A002465F159004587CE /* ComPspdfkitModuleAssets.m */, 97 | 3AFF69FB2465F159004587CE /* ComPspdfkitPrefix.pch */, 98 | 3AFF6A022465F159004587CE /* ComPspdfkitView.h */, 99 | 3AFF69F92465F159004587CE /* ComPspdfkitView.m */, 100 | 3AFF69F62465F159004587CE /* ComPspdfkitViewProxy.h */, 101 | 3AFF69F42465F159004587CE /* ComPspdfkitViewProxy.m */, 102 | 3AFF6A012465F159004587CE /* PSPDFUtils.h */, 103 | 3AFF69F32465F159004587CE /* PSPDFUtils.m */, 104 | 3AFF69F22465F159004587CE /* TIPSPDFAnnotationProxy.h */, 105 | 3AFF69F72465F159004587CE /* TIPSPDFAnnotationProxy.m */, 106 | 3AFF69FF2465F159004587CE /* TIPSPDFViewController.h */, 107 | 3AFF69FD2465F159004587CE /* TIPSPDFViewController.m */, 108 | 3AFF69F82465F159004587CE /* TIPSPDFViewControllerProxy.h */, 109 | 3AFF69FA2465F159004587CE /* TIPSPDFViewControllerProxy.m */, 110 | ); 111 | path = Classes; 112 | sourceTree = ""; 113 | }; 114 | 32C88DFF0371C24200C91783 /* Other Sources */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 24DD6D1B1134B66800162E58 /* titanium.xcconfig */, 118 | ); 119 | name = "Other Sources"; 120 | sourceTree = ""; 121 | }; 122 | 3ACB3FF82541C690000561FC /* Frameworks */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 3ACB3FF92541C690000561FC /* PSPDFKit.xcframework */, 126 | 3ACB3FFA2541C691000561FC /* PSPDFKitUI.xcframework */, 127 | ); 128 | name = Frameworks; 129 | sourceTree = ""; 130 | }; 131 | /* End PBXGroup section */ 132 | 133 | /* Begin PBXHeadersBuildPhase section */ 134 | D2AAC07A0554694100DB518D /* Headers */ = { 135 | isa = PBXHeadersBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | 3AFF6A122465F159004587CE /* PSPDFUtils.h in Headers */, 139 | 3AFF6A132465F159004587CE /* ComPspdfkitView.h in Headers */, 140 | 3AFF6A0C2465F159004587CE /* ComPspdfkitPrefix.pch in Headers */, 141 | 3AFF6A032465F159004587CE /* TIPSPDFAnnotationProxy.h in Headers */, 142 | 3AFF6A0F2465F159004587CE /* ComPspdfkitModuleAssets.h in Headers */, 143 | 3AFF6A0D2465F159004587CE /* ComPspdfkitModule.h in Headers */, 144 | 3AFF6A072465F159004587CE /* ComPspdfkitViewProxy.h in Headers */, 145 | 3AFF6A092465F159004587CE /* TIPSPDFViewControllerProxy.h in Headers */, 146 | 3AFF6A102465F159004587CE /* TIPSPDFViewController.h in Headers */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | /* End PBXHeadersBuildPhase section */ 151 | 152 | /* Begin PBXNativeTarget section */ 153 | D2AAC07D0554694100DB518D /* pspdfkit */ = { 154 | isa = PBXNativeTarget; 155 | buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "pspdfkit" */; 156 | buildPhases = ( 157 | D2AAC07A0554694100DB518D /* Headers */, 158 | D2AAC07B0554694100DB518D /* Sources */, 159 | D2AAC07C0554694100DB518D /* Frameworks */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | ); 165 | name = pspdfkit; 166 | productName = pspdfkit; 167 | productReference = D2AAC07E0554694100DB518D /* libComPspdfkit.a */; 168 | productType = "com.apple.product-type.library.static"; 169 | }; 170 | /* End PBXNativeTarget section */ 171 | 172 | /* Begin PBXProject section */ 173 | 0867D690FE84028FC02AAC07 /* Project object */ = { 174 | isa = PBXProject; 175 | attributes = { 176 | LastUpgradeCheck = 1200; 177 | ORGANIZATIONNAME = "PSPDFKit GmbH"; 178 | }; 179 | buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "PSPDFKit" */; 180 | compatibilityVersion = "Xcode 3.2"; 181 | developmentRegion = en; 182 | hasScannedForEncodings = 1; 183 | knownRegions = ( 184 | en, 185 | Base, 186 | ); 187 | mainGroup = 0867D691FE84028FC02AAC07 /* pspdfkit */; 188 | productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; 189 | projectDirPath = ""; 190 | projectRoot = ""; 191 | targets = ( 192 | D2AAC07D0554694100DB518D /* pspdfkit */, 193 | ); 194 | }; 195 | /* End PBXProject section */ 196 | 197 | /* Begin PBXSourcesBuildPhase section */ 198 | D2AAC07B0554694100DB518D /* Sources */ = { 199 | isa = PBXSourcesBuildPhase; 200 | buildActionMask = 2147483647; 201 | files = ( 202 | 3AFF6A0B2465F159004587CE /* TIPSPDFViewControllerProxy.m in Sources */, 203 | 3AFF6A112465F159004587CE /* ComPspdfkitModuleAssets.m in Sources */, 204 | 3AFF6A062465F159004587CE /* ComPspdfkitModule.m in Sources */, 205 | 3AFF6A052465F159004587CE /* ComPspdfkitViewProxy.m in Sources */, 206 | 3AFF6A0E2465F159004587CE /* TIPSPDFViewController.m in Sources */, 207 | 3AFF6A0A2465F159004587CE /* ComPspdfkitView.m in Sources */, 208 | 3AFF6A082465F159004587CE /* TIPSPDFAnnotationProxy.m in Sources */, 209 | 3AFF6A042465F159004587CE /* PSPDFUtils.m in Sources */, 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | }; 213 | /* End PBXSourcesBuildPhase section */ 214 | 215 | /* Begin XCBuildConfiguration section */ 216 | 1DEB921F08733DC00010E9CD /* Debug */ = { 217 | isa = XCBuildConfiguration; 218 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 219 | buildSettings = { 220 | CLANG_ENABLE_MODULES = YES; 221 | COPY_PHASE_STRIP = NO; 222 | DSTROOT = /tmp/ComPspdfkit.dst; 223 | GCC_MODEL_TUNING = G5; 224 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 225 | GCC_PREFIX_HEADER = "$(PROJECT_DIR)/Classes/ComPspdfkitPrefix.pch"; 226 | INSTALL_PATH = /usr/local/lib; 227 | PRODUCT_NAME = ComPspdfkit; 228 | }; 229 | name = Debug; 230 | }; 231 | 1DEB922308733DC00010E9CD /* Debug */ = { 232 | isa = XCBuildConfiguration; 233 | buildSettings = { 234 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 235 | CLANG_ENABLE_OBJC_ARC = YES; 236 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 237 | CLANG_WARN_BOOL_CONVERSION = YES; 238 | CLANG_WARN_COMMA = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 241 | CLANG_WARN_EMPTY_BODY = YES; 242 | CLANG_WARN_ENUM_CONVERSION = YES; 243 | CLANG_WARN_INFINITE_RECURSION = YES; 244 | CLANG_WARN_INT_CONVERSION = YES; 245 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 246 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 247 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 248 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 249 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 250 | CLANG_WARN_STRICT_PROTOTYPES = YES; 251 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 252 | CLANG_WARN_UNREACHABLE_CODE = YES; 253 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 254 | DEAD_CODE_STRIPPING = NO; 255 | ENABLE_BITCODE = NO; 256 | ENABLE_STRICT_OBJC_MSGSEND = YES; 257 | GCC_NO_COMMON_BLOCKS = YES; 258 | GCC_OPTIMIZATION_LEVEL = 0; 259 | GCC_PREPROCESSOR_DEFINITIONS = TITANIUM; 260 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 261 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 262 | GCC_WARN_UNDECLARED_SELECTOR = YES; 263 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 264 | GCC_WARN_UNUSED_FUNCTION = YES; 265 | GCC_WARN_UNUSED_VARIABLE = YES; 266 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 267 | ONLY_ACTIVE_ARCH = YES; 268 | OTHER_LDFLAGS = "-fobjc-arc-exceptions"; 269 | SDKROOT = iphoneos; 270 | TARGETED_DEVICE_FAMILY = "1,2"; 271 | }; 272 | name = Debug; 273 | }; 274 | 78A35E41149F55240039CE92 /* Release */ = { 275 | isa = XCBuildConfiguration; 276 | buildSettings = { 277 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 278 | CLANG_ENABLE_OBJC_ARC = YES; 279 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 280 | CLANG_WARN_BOOL_CONVERSION = YES; 281 | CLANG_WARN_COMMA = YES; 282 | CLANG_WARN_CONSTANT_CONVERSION = YES; 283 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 284 | CLANG_WARN_EMPTY_BODY = YES; 285 | CLANG_WARN_ENUM_CONVERSION = YES; 286 | CLANG_WARN_INFINITE_RECURSION = YES; 287 | CLANG_WARN_INT_CONVERSION = YES; 288 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 289 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 290 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 291 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 292 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 293 | CLANG_WARN_STRICT_PROTOTYPES = YES; 294 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 295 | CLANG_WARN_UNREACHABLE_CODE = YES; 296 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 297 | DEAD_CODE_STRIPPING = NO; 298 | ENABLE_BITCODE = NO; 299 | ENABLE_STRICT_OBJC_MSGSEND = YES; 300 | GCC_NO_COMMON_BLOCKS = YES; 301 | GCC_OPTIMIZATION_LEVEL = fast; 302 | GCC_PREPROCESSOR_DEFINITIONS = TITANIUM; 303 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 304 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 305 | GCC_WARN_UNDECLARED_SELECTOR = YES; 306 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 307 | GCC_WARN_UNUSED_FUNCTION = YES; 308 | GCC_WARN_UNUSED_VARIABLE = YES; 309 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 310 | OTHER_LDFLAGS = "-fobjc-arc-exceptions"; 311 | SDKROOT = iphoneos; 312 | TARGETED_DEVICE_FAMILY = "1,2"; 313 | }; 314 | name = Release; 315 | }; 316 | 78A35E42149F55240039CE92 /* Release */ = { 317 | isa = XCBuildConfiguration; 318 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 319 | buildSettings = { 320 | CLANG_ENABLE_MODULES = YES; 321 | DSTROOT = /tmp/ComPspdfkit.dst; 322 | GCC_MODEL_TUNING = G5; 323 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 324 | GCC_PREFIX_HEADER = "$(PROJECT_DIR)/Classes/ComPspdfkitPrefix.pch"; 325 | INSTALL_PATH = /usr/local/lib; 326 | PRODUCT_NAME = ComPspdfkit; 327 | }; 328 | name = Release; 329 | }; 330 | /* End XCBuildConfiguration section */ 331 | 332 | /* Begin XCConfigurationList section */ 333 | 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "pspdfkit" */ = { 334 | isa = XCConfigurationList; 335 | buildConfigurations = ( 336 | 1DEB921F08733DC00010E9CD /* Debug */, 337 | 78A35E42149F55240039CE92 /* Release */, 338 | ); 339 | defaultConfigurationIsVisible = 0; 340 | defaultConfigurationName = Debug; 341 | }; 342 | 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "PSPDFKit" */ = { 343 | isa = XCConfigurationList; 344 | buildConfigurations = ( 345 | 1DEB922308733DC00010E9CD /* Debug */, 346 | 78A35E41149F55240039CE92 /* Release */, 347 | ); 348 | defaultConfigurationIsVisible = 0; 349 | defaultConfigurationName = Debug; 350 | }; 351 | /* End XCConfigurationList section */ 352 | }; 353 | rootObject = 0867D690FE84028FC02AAC07 /* Project object */; 354 | } 355 | -------------------------------------------------------------------------------- /PSPDFKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PSPDFKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PSPDFKit.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Latest 7 | PreviewsEnabled 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /PSPDFKit.xcodeproj/xcshareddata/xcschemes/PSPDFKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Axway Appcelerator End-of-Support Notice 2 | 3 | Axway announced [End-of-Support for the Titanium SDK](https://devblog.axway.com/featured/product-update-changes-to-application-development-services-appcelerator/) effective 1 March 2022. PSPDFKit stopped updating and supporting the PSPDFKit for iOS Titanium Module as of 1 March 2021. The module is open source, so if you require more time to transition to a different platform, it can still be updated and customized to your needs. 4 | 5 | # PSPDFKit for iOS Titanium Module 6 | 7 | **[PSPDFKit for iOS](https://pspdfkit.com/pdf-sdk/ios) — the best way to handle PDF documents on iOS.** A high-performance viewer, extensive annotation and document editing tools, digital signatures, and more. All engineered for the best possible user and developer experience. 8 | 9 | PSPDFKit for iOS Titanium module requires a valid license of PSPDFKit for iOS. You can [request a trial license here](https://pspdfkit.com/try). PSPDFKit 10.2 for iOS requires Xcode 12.4 or later and supports iOS 12 or later. Read more about version support [in our guides](https://pspdfkit.com/guides/ios/current/announcements/version-support). Check out [our changelog](https://pspdfkit.com/changelog/ios) to learn more about the releases. 10 | 11 | ## Support, Issues and License Questions 12 | 13 | PSPDFKit offers support for customers with an active SDK license via [our support platform](https://pspdfkit.com/support/request). 14 | 15 | Are you evaluating our SDK? That's great, we're happy to help out! To make sure this is fast, please use a work email and have someone from your company fill out [our sales form](https://pspdfkit.com/sales). 16 | 17 | ## Getting Started 18 | 19 | In the following steps, you will build PSPDFKit for iOS Titanium module using Titanium CLI. 20 | 21 | ### Configuring the Environment 22 | 23 | First, make sure the Xcode Command-Line Tools are installed and their license is accepted: 24 | 25 | ```bash 26 | # Install Xcode Command-Line Tools. 27 | $ sudo xcode-select --install 28 | 29 | # Accept EULA once installed. 30 | $ sudo xcodebuild -license accept 31 | ``` 32 | 33 | Then, let's clone the module source: 34 | 35 | ```bash 36 | # Go to your workspace directory. 37 | $ cd ~/path/to/workspace 38 | 39 | # Clone the PSPDFKit Titanium Module. 40 | $ git clone https://github.com/PSPDFKit/Appcelerator-iOS.git 41 | ``` 42 | 43 | From inside the cloned directory, install the remaining required tools: 44 | 45 | ```bash 46 | # Go to the cloned directory. 47 | $ cd Appcelerator-iOS 48 | 49 | # Make sure you're using Node 12. 50 | # You can use nvm to manage multiple Node versions. 51 | $ nvm use 12 52 | 53 | # Install CocoaPods globally. 54 | $ gem install cocoapods 55 | 56 | # Install local build script dependencies. 57 | $ npm install 58 | ``` 59 | 60 | ### Building the Module 61 | 62 | From inside the cloned directory, run the build script which will first make sure that all required tools are installed and then build the PSPDFKit for iOS Titanium module. By default, it will use the Titanium SDK version declared in the [manifest](manifest) file but you can force it to use a different one using `--sdk` option. 63 | 64 | ```bash 65 | # Build the PSPDFKit Titanium module. 66 | $ node scripts/bootstrap.js build 67 | ``` 68 | 69 | Should something go wrong along the way, run the build script again with `--verbose` option to enable additional logging. You can also use `--help` to learn all available commands and options in the build script. 70 | 71 | ### Importing the Module 72 | 73 | In the following steps, you will create an example Appcelerator app and import PSPDFKit for iOS Titanium module you built above. If you have an existing Appcelerator project already, you can skip this section. 74 | 75 | First, you need to set up the Appcelerator environment: 76 | 77 | ```bash 78 | # Go back to your workspace directory. 79 | $ cd .. 80 | 81 | # Make sure you're using Node 12. 82 | # You can use nvm to manage multiple Node versions. 83 | $ nvm use 12 84 | 85 | # Install Appcelerator and Titanium CLIs globally. 86 | $ npm install -g appcelerator titanium 87 | 88 | # Run through the basic Appcelerator setup. Requires you to log in. 89 | $ appc setup 90 | 91 | # Install and select the latest Appcelerator SDK. 92 | $ appc use latest 93 | ``` 94 | 95 | Then, create a new Appcelerator app: 96 | 97 | ```bash 98 | # Create a new app from template. 99 | $ appc new --type app --name MyApp --id com.example.MyApp 100 | ``` 101 | 102 | Open `MyApp/tiapp.xml` and include PSPDFKit for iOS Titanium module. You can ask the build script for the values of PSPDFKit version, Titanium SDK version and iOS deployment target by running `node scripts/bootstrap.js versions` from inside the cloned directory. 103 | 104 | ```diff 105 | 106 | ... 107 | 108 | + IOS_DEPLOYMENT_TARGET 109 | 110 | ... 111 | 112 | + com.pspdfkit 113 | 114 | ... 115 | + TITANIUM_SDK_VERSION 116 | ... 117 | 118 | ``` 119 | 120 | Copy our example into your app: 121 | 122 | ```bash 123 | # Avoid using the Alloy framework for now. 124 | $ mv MyApp/app MyApp/alloy 125 | 126 | # Copy our example code into place. 127 | $ cp -R Appcelerator-iOS/example MyApp/Resources 128 | ``` 129 | 130 | Open `MyApp/Resources/app.js` and update your license key, which you can retrieve from the [customer portal](https://customers.pspdfkit.com/customers/sign_in). 131 | 132 | ```diff 133 | // You need to activate your PSPDFKit before you can use it. 134 | // Follow the instructions in the email you get after licensing the framework. 135 | - pspdfkit.setLicenseKey("LICENSE_KEY_GOES_HERE"); 136 | + pspdfkit.setLicenseKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); 137 | ``` 138 | 139 | Finally, it's time to run the app. Running the build command with an empty `--device-id` will cause Titanium CLI to list all available simulator devices and ask you to choose one. 140 | 141 | ```bash 142 | # Go to the app directory. 143 | $ cd MyApp 144 | 145 | # Build and run the app. 146 | $ titanium build --platform ios --device-id 147 | ``` 148 | 149 | Should something go wrong with the build, add `--log-level trace` to the above command to enable additional logging. Alternatively, you can also prefix it with `DEBUG=*` (like `DEBUG=* titanium build...`). 150 | 151 | ## Using the Module 152 | 153 | To use PSPDFKit for iOS Titanium module in your code, require it first and then create a view using `createView` function: 154 | 155 | ```js 156 | const PSPDFKit = require("com.pspdfkit") 157 | 158 | const view = PSPDFKit.createView({ 159 | filename: "PSPDFKit.pdf", 160 | options: { 161 | pageMode: 0, // PSPDFPageModeSingle 162 | pageTransition: 2, // PSPDFPageCurlTransition 163 | }, 164 | documentOptions: { 165 | title: "Custom Title", 166 | }, 167 | }) 168 | ``` 169 | 170 | Please refer to the documentation comments in [ComPspdfkitModule.h](Classes/ComPspdfkitModule.h) and [ComPspdfkitViewProxy.h](Classes/ComPspdfkitViewProxy.h) to learn more about the available API. 171 | 172 | ## Troubleshooting 173 | 174 | ### Errors While Running the Build Script 175 | 176 | ```none 177 | internal/modules/cjs/loader.js:983 178 | throw err; 179 | ^ 180 | 181 | Error: Cannot find module 'chalk' 182 | ``` 183 | 184 | The build script requires a couple of handy Node.js modules to work. If you see a similar error when running the build script, make sure to run `npm install` first in the cloned directory. See [configuring the environment](#configuring-the-environment) section to learn more. 185 | 186 | ### Using Different Versions of Titanium SDK 187 | 188 | The Titanium SDK version you use to build the module must be the same version that you use to build your app (specified by `` in `tiapp.xml`). You can explicitly set the Titanium SDK version the module is built against by passing `--sdk` option to the build script: 189 | 190 | ```bash 191 | # Use Titanium SDK 9.0.0.GA to build the module. 192 | $ node script/bootstrap.js build --sdk 9.0.0.GA 193 | ``` 194 | 195 | If you want to install the minimal version that PSPDFKit for iOS Titanium module requires, you can ask the build script for it: 196 | 197 | ```bash 198 | # Install the minimal required version of Titanium SDK. 199 | $ titanium sdk install $(node scripts/bootstrap.js versions --just titanium) 200 | ``` 201 | 202 | ### Using Local Titanium Binaries 203 | 204 | If you're unable to install Titanium or Appcelerator CLI globally using `npm install -g`, you can instead rely on the local binaries by taking advantage of [npx](https://www.npmjs.com/package/npx). Just prefix all your `titanium` and `appc` commands with `npx`: 205 | 206 | ```bash 207 | # Use the locally installed Titanium CLI binary. 208 | $ npx titanium --help 209 | 210 | # Use the locally installed Appcelerator CLI binary. 211 | $ npx appc --help 212 | ``` 213 | 214 | ### Build Errors 215 | 216 | ```none 217 | [ERROR] ** BUILD FAILED ** 218 | [ERROR] The following build commands failed: 219 | [ERROR] Ld build/Products/Debug-iphonesimulator/MyApp.app/MyApp normal x86_64 220 | [ERROR] (1 failure) 221 | ``` 222 | 223 | If you see a similar error when running the project, it's likely because the PSPDFKit for iOS Titanium module was not included in your project's `tiapp.xml`. Please refer to [importing the module](#importing-the-module) section to learn more. 224 | 225 | ## License 226 | 227 | This project can be used for evaluation or if you have a valid PSPDFKit for iOS license. All items and source code Copyright © 2010-2021 PSPDFKit GmbH. See [LICENSE](LICENSE) for details. 228 | 229 | ## Contributing 230 | 231 | Please make sure [you signed our Contributor License Agreement](https://pspdfkit.com/guides/web/current/miscellaneous/contributing/) so we can accept your contributions. 232 | -------------------------------------------------------------------------------- /example/PSPDFKit.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PSPDFKit/Appcelerator-iOS/f547c27823cb9352681977b2114dd360e3ae2eb2/example/PSPDFKit.pdf -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | PSPDFKit Appcelerator Sample Code 3 | 4 | To synchronize popovers, you can listen for the "didPresentPopover" event that PSPDFKit fires after opening a popover. 5 | */ 6 | 7 | // open a single window 8 | var window = Ti.UI.createWindow({ 9 | backgroundColor : 'white' 10 | }); 11 | window.orientationModes = [Titanium.UI.PORTRAIT, Titanium.UI.LANDSCAPE_LEFT, Titanium.UI.LANDSCAPE_RIGHT]; 12 | 13 | // Include the PSPDFKit module. 14 | var pspdfkit = require('com.pspdfkit'); 15 | Ti.API.info("module is => " + pspdfkit); 16 | 17 | // You need to activate your PSPDFKit before you can use it. 18 | // Follow the instructions in the email you get after licensing the framework. 19 | pspdfkit.setLicenseKey("LICENSE_KEY_GOES_HERE"); 20 | 21 | // increase log level (only needed for debugging) 22 | // Log level 0 (nothing) to 4 (verbose) are available. 23 | pspdfkit.setLogLevel(3); 24 | 25 | // add custom language additions. Optional. 26 | // A lot of languages are built-in but can be overridden here. 27 | // This dictionary is just provided so you can easily look up the words. 28 | // See all strings at PSPDFKit.framework/PSPDFKit.bundle/en.lproj 29 | pspdfkit.setLanguageDictionary({ 30 | "en" : { 31 | // general 32 | "Table Of Contents" : "Outline", 33 | "Go to %@" : "%@", 34 | }, 35 | "de" : { 36 | "Grid" : "Übersicht" 37 | } 38 | }); 39 | 40 | // Use if you want to clear the cache. Usually not needed. 41 | //pspdfkit.clearCache(); 42 | 43 | // allows you to pre-cache a document. 44 | // This call is not needed and purely optional if you want to prepare rendering before showing. 45 | // (e.g. call directly after downloading) 46 | pspdfkit.cacheDocument('PSPDFKit.pdf'); 47 | 48 | // You can selectively delete the cache for a document using 49 | //pspdfkit.removeCacheForDocument('PSPDFKit.pdf'); 50 | 51 | // and stops the background caching process. 52 | //pspdfkit.stopCachingDocument('PSPDFKit.pdf'); 53 | 54 | // Get rendered image for the document and page. Parameter 3 is 0 for full-size and 1 for thumbnail. 55 | var image = pspdfkit.imageForDocument('PSPDFKit.pdf', 0, 1); 56 | Ti.API.info("image: " + image); 57 | 58 | // Define Modal Test Button 59 | var modalButton = Titanium.UI.createButton({ 60 | title : 'Open modal view', 61 | top : 10, 62 | height : 35, 63 | left : 20, 64 | right : 20 65 | }); 66 | 67 | var navButton = Ti.UI.createButton({ 68 | title : 'Custom', 69 | backgroundColor : '#ae4041', 70 | color : '#ffffff', 71 | style : Titanium.UI.iOS.SystemButtonStyle.BORDERED, 72 | height : 24 73 | }); 74 | 75 | navButton.addEventListener('click', function(e) { 76 | alert("Custom Titanium UIBarButton pressed"); 77 | }); 78 | 79 | modalButton.addEventListener('click', function(e) { 80 | // Replace PSPDFKit.pdf with your own pdf. 81 | // Copy the PDF to include to the Resources folder (where app.js is) 82 | // After copying a new PDF, clean the project and then rebuild. 83 | var pdfController = pspdfkit.showPDFAnimated('PSPDFKit.pdf', 4, // animation option: 0 = no animation, 1 = default animation, 2 = UIModalTransitionStyleCoverVertical, 3 = UIModalTransitionStyleFlipHorizontal, 4 = UIModalTransitionStyleCrossDissolve 84 | // http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html 85 | { 86 | pageMode : 0, // PSPDFPageModeSingle 87 | pageTransition : 2, // PSPDFPageCurlTransition 88 | linkAction : 3, // PSPDFLinkActionInlineBrowser (new default) 89 | thumbnailSize: [200, 200], // Allows custom thumbnail size. 90 | 91 | // toolbar config: see https://pspdfkit.com/api/ios/Classes/PSPDFViewController.html#/c:objc(cs)PSPDFViewController(py)outlineButtonItem for built in options. 92 | // Built in options are sent via strings. Invalid strings will simply be ignored. 93 | leftBarButtonItems : ["closeButtonItem"], 94 | rightBarButtonItems : [navButton, "thumbnailsButtonItem"], 95 | 96 | additionalBarButtonItems : ["openInButtonItem", "emailButtonItem", "printButtonItem", "searchButtonItem", "outlineButtonItem", "annotationButtonItem"] // text list, does *not* support custom buttons. 97 | 98 | // pageMode values 0=single page, 1=double page, 2=automatic 99 | // some supported properties 100 | // see https://pspdfkit.com/api/ios/Classes/PSPDFViewController.html 101 | /* firstPageAlwaysSingle: true, 102 | * page" : 3, 103 | * pageScrolling" : 1, 104 | * zoomingSmallDocumentsEnabled : false, 105 | * fitToWidthEnabled : false, 106 | * maximumZoomScale : 1.3, 107 | * pagePadding : 80, 108 | * shadowEnabled : false, 109 | * backgroundColor : "#FF0000", */ 110 | }, { 111 | title : "My custom modal document title", 112 | }); 113 | 114 | // get current document path 115 | //var documentPath = pdfController.documentPath; 116 | 117 | //pdfController.showCloseButton = false; 118 | 119 | //pdfController.editableAnnotationTypes : ["Highlight", "Ink"] // Allows you to limit the editable annotation types 120 | 121 | // Set the link annotation border width 122 | pdfController.linkAnnotationStrokeWidth = 1.0; 123 | // Changes the link annotation colors to a light red (first hex pair is optional alpha) (or use "clear" to hide) 124 | pdfController.linkAnnotationBorderColor = "#33FF0000"; // Border Color 125 | pdfController.linkAnnotationHighlightColor = "99FF0000"; // Highlight Color 126 | pdfController.thumbnailFilterOptions = ["All", "Bookmarks"]; // "Annotations" is the third possibility. If <= 1 option, the filter will not be displayed. 127 | pdfController.outlineControllerFilterOptions = ["Outline"]; // "Outline", "Bookmarks" and "Annotations" are supported. 128 | 129 | // example how to hide the top left close button 130 | //pdfView.showCloseButton = false; 131 | 132 | // You can save annotations using: 133 | pdfController.saveAnnotations(); 134 | 135 | // PSPDFKit also allows you to define the annotation save destination: 136 | // PSPDFAnnotationSaveModeDisabled = 0 137 | // PSPDFAnnotationSaveModeExternalFile = 1, // will use save/loadAnnotationsWithError of PSPDFAnnotationParser (override to ship your own) 138 | // PSPDFAnnotationSaveModeEmbedded = 2, 139 | // PSPDFAnnotationSaveModeEmbeddedWithExternalFileAsFallback = 3 // Default. 140 | //pdfController.setAnnotationSaveMode(1); 141 | 142 | // most properties can also be changed at runtime 143 | // here we start scrolling to page 4 right upon starting 144 | //pdfController.scrollToPage(4, true); 145 | 146 | Ti.API.log('current page: ' + pdfController.page + ' total pages: ' + pdfController.totalPages); 147 | 148 | // you can also enable/disable the user's ability to scroll pages 149 | //pdfController.scrollingEnabled = false; 150 | 151 | // close controller after 2 seconds 152 | //var closeController = function() { pdfController.close(true) }; 153 | //setTimeout(closeController, 5000); 154 | 155 | // example how to register for the didShowPage event 156 | pdfController.addEventListener("didShowPage", function(e) { 157 | Ti.API.log('(modal) didShowPage: ' + e.page); 158 | }); 159 | 160 | // add event that listens when a viewController will be closed 161 | pdfController.addEventListener('willCloseController', function(e) { 162 | Ti.API.log('(modal) willCloseController... we\'re on the edge of being killed.'); 163 | }); 164 | // add event after view controller has been closed 165 | pdfController.addEventListener('didCloseController', function(e) { 166 | alert("PSPDFKit ViewController closed."); 167 | }); 168 | // add event listener for annotations. 169 | pdfController.addEventListener('didTapOnAnnotation', function(dict) { 170 | Ti.API.log("didTapOnAnnotation " + dict.siteLinkTarget); 171 | }); 172 | 173 | pdfController.addEventListener('willShowHUD', function(dict) { 174 | Ti.API.log("willShowHUD - show your custom HUD"); 175 | }); 176 | 177 | pdfController.addEventListener('willHideHUD', function(dict) { 178 | Ti.API.log("willHideHUD - hide your custom HUD"); 179 | }); 180 | // set a callback on annotation events. 181 | // return true if you have processed the annotation yourself 182 | // note: you can't create UI elements here, as this is a callback with a different context 183 | // use the didTapOnAnnotation-event that's called afterwards. 184 | pdfController.setDidTapOnAnnotationCallback(function(dict) { 185 | Ti.API.log('(modal) didTapOnAnnotationCallback page:' + dict.page + ', url: ' + dict.siteLinkTarget); 186 | return false; 187 | // let PSPDFKit handle it 188 | }); 189 | 190 | }); 191 | 192 | var appceleratorTestButton = Ti.UI.createButton({ 193 | title : 'Appcelerator', 194 | backgroundColor : '#cccccc', 195 | color : '#ffffff', 196 | style : Titanium.UI.iOS.SystemButtonStyle.BORDERED, 197 | width : 100 198 | }); 199 | appceleratorTestButton.addEventListener('click', function(e) { 200 | alert("Custom appceleratorTestButton pressed."); 201 | }); 202 | 203 | // add inline view - works almost equal to showPDFAnimated. 204 | var pdfView = pspdfkit.createView({ 205 | top : 200, 206 | right : 0, 207 | bottom : 0, 208 | left : 0, 209 | filename : 'PSPDFKit.pdf', 210 | options : { 211 | pageMode : 0, 212 | // close button is automatically hidden here 213 | leftBarButtonItems : [appceleratorTestButton] 214 | }, 215 | documentOptions : { 216 | title : "Custom Title Here" 217 | } 218 | }); 219 | 220 | pdfView.setAnnotationSaveMode(1); 221 | pdfView.thumbnailFilterOptions = null; // Remove filters. 222 | 223 | window.add(pdfView); 224 | 225 | // example how to start a search. 226 | // Window needs to be completely visible before calling this (thus the delay) 227 | setTimeout(function() { 228 | pdfView.searchForString("iOS", true); 229 | }, 1000); 230 | 231 | // to coordinate the internal popovers with the view, you can use hidePopover(true) (true/false is the animation value) 232 | //pdfView.hidePopover(true); 233 | 234 | // example how to register for the didShowPage event 235 | pdfView.addEventListener("didShowPage", function(e) { 236 | Ti.API.log('didShowPage: ' + e.page); 237 | }); 238 | 239 | // example how to lock scrolling 240 | //pdfView.scrollingEnabled = false; 241 | //pdfView.scrollToPage(2, false); 242 | 243 | Ti.API.log('pdfView: current page: ' + pdfView.page + ' total pages: ' + pdfView.totalPages); 244 | 245 | // Define an action button 246 | var button2 = Titanium.UI.createButton({ 247 | title : 'Scroll to page 5', 248 | top : 55, 249 | height : 35, 250 | left : 20, 251 | right : 20 252 | }); 253 | 254 | button2.addEventListener('click', function(e) { 255 | pdfView.scrollToPage(5, true); 256 | pdfView.setViewMode(1, true); 257 | // show thumbnails is 1, page is 0. 258 | }); 259 | 260 | var button3 = Titanium.UI.createButton({ 261 | title : 'Push controller', 262 | top : 100, 263 | height : 35, 264 | left : 20, 265 | right : 20 266 | }); 267 | 268 | // create a NavigationWindow, push "first" window onto it 269 | var navigationWindow = Titanium.UI.iOS.createNavigationWindow({ 270 | window : window 271 | }); 272 | 273 | // add a click event to the label which will create a new window with a PSPDFKit view and push it onto the nav group 274 | button3.addEventListener("click", function(e) { 275 | 276 | // create "second" window 277 | var pushedWindow = Ti.UI.createWindow({ 278 | backgroundColor : "#fff", 279 | title : "Pushed Controller", 280 | }); 281 | 282 | // create PSPDFKit view 283 | var pdfView = pspdfkit.createView({ 284 | filename : 'protected.pdf', 285 | options : { 286 | pageMode : 0, 287 | useParentNavigationBar : true, 288 | }, 289 | documentOptions : { 290 | password : "test123" 291 | } 292 | }); 293 | 294 | // add PSPDFKit view to second window and push second window to nav group 295 | pushedWindow.add(pdfView); 296 | navigationWindow.openWindow(pushedWindow, {animated:true}); 297 | }); 298 | 299 | 300 | var button4 = Titanium.UI.createButton({ 301 | title : 'Push controller with custom toolbar', 302 | top : 145, 303 | height : 35, 304 | left : 20, 305 | right : 20 306 | }); 307 | 308 | 309 | button4.addEventListener("click", function(e) { 310 | 311 | var pushedWindow = Ti.UI.createWindow({ 312 | barColor:"#222" 313 | }); 314 | 315 | //////////////////////////////// 316 | // custom toolbar 317 | var customToolbarView = Ti.UI.createView({ 318 | top:20, 319 | left:5, 320 | right:5, 321 | height:30, 322 | backgroundColor:"white", 323 | opacity:0.8 324 | }); 325 | 326 | var viewMode = Ti.UI.createButtonBar({ 327 | labels:['Close', 'Search', 'Share', 'Bookmark', 'Outline'], 328 | backgroundColor:"#222", 329 | style:Ti.UI.iOS.SystemButtonStyle.BAR 330 | }); 331 | customToolbarView.add(viewMode); 332 | 333 | viewMode.addEventListener("click", function(e){ 334 | switch(e.index){ 335 | case 0: 336 | //close the window 337 | pushedWindow.close(); 338 | break; 339 | case 1: 340 | //show the search 341 | pdfView.showSearchView(viewMode); 342 | break; 343 | case 2: 344 | // show the activity view for sharing 345 | pdfView.showActivityView(viewMode); 346 | break; 347 | case 3: 348 | //bookmark the current page 349 | pdfView.bookmarkPage(); 350 | alert("Page has been bookmarked!"); 351 | break; 352 | case 4: 353 | //show the outline 354 | pdfView.showOutlineView(viewMode); 355 | break; 356 | } 357 | }); 358 | 359 | // create PSPDFKit view 360 | var pdfView = pspdfkit.createView({ 361 | filename : 'PSPDFKit.pdf', 362 | options : { 363 | pageMode : 0, 364 | useParentNavigationBar : true, 365 | }, documentOptions : { 366 | title : "" 367 | }}); 368 | 369 | pdfView.addEventListener('willShowHUD', function(dict) { 370 | customToolbarView.animate({opacity:0.8, duration:350}); 371 | }); 372 | 373 | pdfView.addEventListener('willHideHUD', function(dict) { 374 | customToolbarView.animate({opacity:0, duration:350}); 375 | }); 376 | 377 | //add your custom toolbar to the pdfView 378 | pdfView.add(customToolbarView); 379 | pushedWindow.add(pdfView); 380 | pushedWindow.open(); 381 | }); 382 | 383 | // example interval to always show current page in log 384 | setInterval(function() { 385 | Ti.API.log('current page: ' + pdfView.page + ' total pages: ' + pdfView.totalPages); 386 | }, 5000); 387 | 388 | window.add(modalButton); 389 | window.add(button2); 390 | window.add(button3); 391 | window.add(button4); 392 | 393 | // example how to download a PDF. 394 | var webDownloadTestButton = Ti.UI.createButton({ 395 | title : 'Download PDF', 396 | style : Titanium.UI.iOS.SystemButtonStyle.BORDERED, 397 | }); 398 | window.setRightNavButton(webDownloadTestButton); 399 | 400 | var fileName = "testdownload.pdf"; 401 | webDownloadTestButton.addEventListener('click', function(e) { 402 | var f = Titanium.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory, fileName); 403 | if (!f.exists()) { 404 | var xhr = Titanium.Network.createHTTPClient({ 405 | onload : function() { 406 | Ti.API.info('PDF downloaded to appDataDirectory/' + fileName); 407 | Ti.App.fireEvent('test_pdf_downloaded', { 408 | filePath : f.nativePath 409 | }); 410 | }, 411 | timeout : 15000 412 | }); 413 | xhr.open('GET', 'https://pspdfkit.com/downloads/case-study-ebriefing.pdf'); 414 | xhr.file = Titanium.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory, fileName); 415 | xhr.send(); 416 | } else { 417 | Ti.App.fireEvent('test_pdf_downloaded', { 418 | filePath : f.nativePath 419 | }); 420 | } 421 | }); 422 | 423 | Ti.App.addEventListener('test_pdf_downloaded', function(e) { 424 | pspdfkit.showPDFAnimated(e.filePath); 425 | }); 426 | 427 | navigationWindow.open(); 428 | -------------------------------------------------------------------------------- /example/protected.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PSPDFKit/Appcelerator-iOS/f547c27823cb9352681977b2114dd360e3ae2eb2/example/protected.pdf -------------------------------------------------------------------------------- /manifest: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | # 4 | # THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | # AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | # UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | # This notice may not be removed from this file. 8 | # 9 | 10 | # The following values will be used by the build script. 11 | 12 | version: 10.2.0 13 | minsdk: 9.3.2.GA 14 | 15 | # Do not edit the below lines. 16 | 17 | name: PSPDFKit 18 | description: PSPDFKit Titanium Module 19 | author: PSPDFKit GmbH 20 | 21 | license: Commercial, see www.PSPDFKit.com 22 | copyright: Copyright (c) 2010-2020 by PSPDFKit GmbH 23 | 24 | platform: iphone 25 | architectures: x86_64 arm64 26 | 27 | moduleid: com.pspdfkit 28 | guid: 3056f4e3-4ee6-4cf3-8417-1d8b8f95853c0 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "engines": { 4 | "node": ">=12.0.0 <13.0.0" 5 | }, 6 | "dependencies": { 7 | "appcelerator": "^5.0.0", 8 | "chalk": "^4.1.0", 9 | "child-process-async": "^1.0.1", 10 | "fs-extra": "^9.1.0", 11 | "lodash": "^4.17.20", 12 | "shell-escape": "^0.2.0", 13 | "titanium": "^5.3.0", 14 | "yargs": "^16.2.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | const _ = require("lodash") 11 | const chalk = require("chalk") 12 | const cp = require("child-process-async") 13 | const fs = require("fs-extra") 14 | const path = require("path") 15 | const shellescape = require("shell-escape") 16 | const yargs = require("yargs") 17 | 18 | /** 19 | * Prepare and build the PSPDFKit Titanium module. 20 | * @param {BuildCommandArgv} argv Arguments to the build command. 21 | * @returns {Promise} Value indicating whether the command succeeded. 22 | */ 23 | async function buildCommand(argv) { 24 | 25 | // These are the shared options we will use in all command spawns. 26 | 27 | const srcroot = path.resolve(__dirname, "../") 28 | const spawnOptions = { cwd: srcroot, verbose: argv.verbose } 29 | 30 | // STEP 1: Determine the required version of PSPDFKit and Titanium SDK based 31 | // on the values in `manifest` file. These versions will be used to compile 32 | // the module later. 33 | 34 | console.log(chalk.blue.bold(`Determining PSPDFKit and Titanium SDK versions...`)) 35 | 36 | // Load the manifest file and look for `version` and `minsdk` keys in it. If 37 | // they're in there, the match variables should contain them at index 1. 38 | 39 | let manifestPath = path.resolve(srcroot, "manifest") 40 | let manifestContents = fs.readFileSync(manifestPath, "utf-8") 41 | 42 | let pspdfkitVersionMatch = manifestContents.match(/version: (.+)\n/) 43 | let titaniumVersionMatch = manifestContents.match(/minsdk: (.+)\n/) 44 | 45 | if (pspdfkitVersionMatch.length != 2 ) { 46 | console.error(chalk.red(`Could not determine required PSPDFKit version.`)) 47 | console.error(chalk.red(`Make sure 'manifest' file contains a valid value for 'version'.`)) 48 | return false 49 | } 50 | 51 | if (titaniumVersionMatch.length != 2 ) { 52 | console.error(chalk.red(`Could not determine required Titanium SDK version.`)) 53 | console.error(chalk.red(`Make sure 'manifest' file contains a valid value for 'minsdk'.`)) 54 | return false 55 | } 56 | 57 | let pspdfkitVersion = pspdfkitVersionMatch[1] 58 | let titaniumVersion = titaniumVersionMatch[1] 59 | 60 | // Overwrite Titanium SDK version from argv if needed. 61 | 62 | if (argv.sdk) { 63 | titaniumVersion = argv.sdk 64 | } 65 | 66 | console.log(chalk.green(`Using PSPDFKit ${pspdfkitVersion} for iOS.`)) 67 | console.log(chalk.green(`Using Titanium SDK ${titaniumVersion}.`)) 68 | 69 | // STEP 2: Make sure `xcodebuild`, `pod` and `titanium` are installed. Also 70 | // make sure that the required Titanium SDK version is installed. 71 | 72 | console.log(chalk.blue.bold(`Making sure all required tools are installed...`)) 73 | 74 | // Check that Xcode CLT are installed. Xcode alone is not enough. 75 | 76 | let xcodeInstalled = await which("xcodebuild", spawnOptions) 77 | 78 | if (!xcodeInstalled) { 79 | console.error(chalk.red(`Xcode Command-Line Tools are not installed.`)) 80 | console.error(chalk.red(`Please run \`sudo xcode-select --install\` first and try again.`)) 81 | return false 82 | } 83 | 84 | // If Xcode license has not been accepted, `xcrun` will fail with a message 85 | // that includes the wotd `license` in it. Homebrew checks for this in the 86 | // same way. 87 | 88 | let xcodeLicenseResult = await spawn("xcrun", ["clang"], _.extend({}, spawnOptions, { validate: false })) 89 | let xcodeLicenseAccepted = xcodeLicenseResult.stderr.toString().match(/license/g) == null 90 | 91 | if (!xcodeLicenseAccepted) { 92 | console.error(chalk.red(`Xcode license has not been accepted.`)) 93 | console.error(chalk.red(`Please run \`sudo xcodebuild -license accept\` first and try again.`)) 94 | return false 95 | } 96 | 97 | let xcodeVersionResult = await spawn("xcodebuild", ["-version"], spawnOptions) 98 | let xcodeVersion = _.trim(xcodeVersionResult.stdout.toString()).match(/Xcode (.+)\n/)[1] 99 | 100 | console.log(chalk.green(`Xcode Command-Line Tools ${xcodeVersion} are installed.`)) 101 | 102 | // Check that CocoaPods is installed globally. 103 | 104 | let cocoapodsInstalled = await which("pod", spawnOptions) 105 | 106 | if (!cocoapodsInstalled) { 107 | console.error(chalk.red(`CocoaPods is not installed.`)) 108 | console.error(chalk.red(`Please run \`gem install cocoapods\` first and try again.`)) 109 | return false 110 | } 111 | 112 | let cocoapodsVersionResult = await spawn("pod", ["--version"], spawnOptions) 113 | let cocoapodsVersion = _.trim(cocoapodsVersionResult.stdout) 114 | 115 | console.log(chalk.green(`CocoaPods ${cocoapodsVersion} is installed.`)) 116 | 117 | // Check that the required Titanium SDK is installed and grab its root. We'll 118 | // use it later to generate xcconfig files. 119 | 120 | let titaniumBin = path.resolve(srcroot, "node_modules", ".bin", "titanium") 121 | 122 | let titaniumSdksResult = await spawn(titaniumBin, ["sdk", "list", "-o", "json"], spawnOptions) 123 | let titaniumSdkroot = JSON.parse(titaniumSdksResult.stdout)["installed"][titaniumVersion] 124 | 125 | if (!_.isString(titaniumSdkroot) || !fs.existsSync(titaniumSdkroot)) { 126 | console.error(chalk.red(`Titanium SDK ${titaniumVersion} is not installed.`)) 127 | console.error(chalk.red(`Please run \`npx titanium sdk install ${titaniumVersion}\` first and try again.`)) 128 | return false 129 | } 130 | 131 | console.log(chalk.green(`Titanium SDK ${titaniumVersion} is installed.`)) 132 | 133 | // STEP 3: Download PSPDFKit and PSPDFKitUI binaries using CocoaPods. To do 134 | // that, we generate a Podfile with the provided customer key and the version 135 | // we determined in step 1. 136 | 137 | console.log(chalk.blue.bold(`Downloading PSPDFKit binaries...`)) 138 | 139 | // Generate a Podfile from the template. 140 | 141 | let podfileTemplatePath = path.resolve(__dirname, "templates", "podfile") 142 | let podfileGeneratedPath = path.resolve(srcroot, "Podfile") 143 | let podfileData = { version: pspdfkitVersion } 144 | 145 | try { 146 | await copyTemplateFile(podfileTemplatePath, podfileGeneratedPath, podfileData) 147 | } catch (error) { 148 | console.error(chalk.red(`Failed to generate a Podfile at '${podfileGeneratedPath}'.`)) 149 | console.error(chalk.red(`Please make sure this script has read-write access to the above location.`)) 150 | return false 151 | } 152 | 153 | console.log(chalk.green(`Generated a Podfile at '${podfileGeneratedPath}'.`)) 154 | 155 | // Let CocoaPods resolve and download PSPDFKit. Podfile is configured to not 156 | // integrate targets so it won't mess up the Xcode project. 157 | 158 | try { 159 | await spawn("pod", ["install", `--project-directory=${srcroot}`, "--no-ansi", "--verbose"], spawnOptions) 160 | } catch (error) { 161 | console.error(chalk.red(`Failed to download PSPDFKit binaries.`)) 162 | console.error(chalk.red(`Please make sure that you have Internet connection.`)) 163 | return false 164 | } 165 | 166 | // Copy PSPDFKit binaries to their expected location. 167 | 168 | let podsPath = path.resolve(srcroot, "Pods", "PSPDFKit") 169 | let pspdfkitSourcePath = path.resolve(podsPath, "PSPDFKit.xcframework") 170 | let pspdfkituiSourcePath = path.resolve(podsPath, "PSPDFKitUI.xcframework") 171 | 172 | let platformPath = path.resolve(srcroot, "platform") 173 | let pspdfkitDestinationPath = path.resolve(platformPath, "PSPDFKit.xcframework") 174 | let pspdfkituiDestinationPath = path.resolve(platformPath, "PSPDFKitUI.xcframework") 175 | 176 | try { 177 | if (fs.pathExistsSync(pspdfkitDestinationPath)) { 178 | fs.rmdirSync(pspdfkitDestinationPath, { recursive: true }) 179 | } 180 | fs.ensureDirSync(pspdfkitDestinationPath) 181 | fs.copySync(pspdfkitSourcePath, pspdfkitDestinationPath) 182 | } catch (error) { 183 | console.error(chalk.red(`Failed to copy PSPDFKit binary from '${pspdfkitSourcePath}' to '${platformPath}'.`)) 184 | console.error(chalk.red(`Please make sure this script has read-write access to the above locations.`)) 185 | return false 186 | } 187 | 188 | try { 189 | if (fs.pathExistsSync(pspdfkituiDestinationPath)) { 190 | fs.rmdirSync(pspdfkituiDestinationPath, { recursive: true }) 191 | } 192 | fs.ensureDirSync(pspdfkituiDestinationPath) 193 | fs.copySync(pspdfkituiSourcePath, pspdfkituiDestinationPath) 194 | } catch (error) { 195 | console.error(chalk.red(`Failed to copy PSPDFKitUI binary from '${pspdfkituiSourcePath}' to '${platformPath}'.`)) 196 | console.error(chalk.red(`Please make sure this script has read-write access to the above locations.`)) 197 | return false 198 | } 199 | 200 | console.log(chalk.green(`Successfully downloaded PSPDFKit binaries to '${platformPath}'.`)) 201 | 202 | // STEP 4: Generate xcconfig files. 203 | 204 | console.log(chalk.blue.bold(`Generating build configuration files...`)) 205 | 206 | // Save the Titanium SDK root to titanium.xcconfig. We use this value in 207 | // search paths. 208 | 209 | let titaniumXcconfigSrcPath = path.resolve(__dirname, "templates", "titanium.xcconfig") 210 | let titaniumXcconfigDstPath = path.resolve(srcroot, "titanium.xcconfig") 211 | let titaniumXcconfigData = { titanium_sdkroot: titaniumSdkroot } 212 | 213 | try { 214 | await copyTemplateFile(titaniumXcconfigSrcPath, titaniumXcconfigDstPath, titaniumXcconfigData) 215 | } catch (error) { 216 | console.error(chalk.red(`Failed to generate a Titanium SDK xcconfig file at '${titaniumXcconfigDstPath}'.`)) 217 | console.error(chalk.red(`Please make sure this script has read-write access to the above location.`)) 218 | return false 219 | } 220 | 221 | console.log(chalk.green(`Generated a Titanium SDK xcconfig file at '${titaniumXcconfigDstPath}'.`)) 222 | 223 | // Steal CocoaPods's OTHER_LDFLAGS into module.xcconfig file since it for 224 | // sure is up-to-date. Titanium will use this file to link the module static 225 | // library later on. 226 | 227 | let cocoapodsXcconfigPath = path.resolve(srcroot, "Pods", "Target Support Files", "PSPDFKit", "PSPDFKit.release.xcconfig") 228 | let cocoapodsXcconfigContents = await fs.readFile(cocoapodsXcconfigPath, "utf-8") 229 | let cocoapodsLdflagsMatch = cocoapodsXcconfigContents.match(/OTHER_LDFLAGS ?= ?(.+)\n/) 230 | 231 | if (cocoapodsLdflagsMatch.length != 2 ) { 232 | console.error(chalk.red(`Could not extract 'OTHER_LDFLAGS' from CocoaPods supporting files.`)) 233 | return false 234 | } 235 | 236 | let ldflagsValue = cocoapodsLdflagsMatch[1] 237 | 238 | let moduleXcconfigSrcPath = path.resolve(__dirname, "templates", "module.xcconfig") 239 | let moduleXcconfigDstPath = path.resolve(srcroot, "module.xcconfig") 240 | let moduleXcconfigData = { other_ldflags: ldflagsValue } 241 | 242 | try { 243 | await copyTemplateFile(moduleXcconfigSrcPath, moduleXcconfigDstPath, moduleXcconfigData) 244 | } catch (error) { 245 | console.error(chalk.red(`Failed to generate a module xcconfig file at '${moduleXcconfigGeneratedPath}'.`)) 246 | console.error(chalk.red(`Please make sure this script has read-write access to the above location.`)) 247 | return false 248 | } 249 | 250 | console.log(chalk.green(`Generated a module xcconfig file at '${moduleXcconfigDstPath}'.`)) 251 | 252 | // STEP 5: Build the Titanium module. 253 | 254 | console.log(chalk.blue.bold("Building PSPDFKit module...")) 255 | 256 | // Delegate compiling to Titanium CLI. Sometimes Titanium CLI will mess up 257 | // the prompt, so we also reset the ANSI escape codes just in case. 258 | 259 | try { 260 | await spawn(titaniumBin, ["build", "--project-dir", srcroot, "--build-only", "--platform", "ios", "--sdk", titaniumVersion, "--log-level", "trace", "--no-banner", "--no-progress-bars", "--no-prompts"], spawnOptions) 261 | process.stdout.write(chalk.reset()) 262 | } catch (error) { 263 | console.error(chalk.red("Failed to build PSPDFKit Titanium module.")) 264 | return false; 265 | } 266 | 267 | console.log(chalk.green(`Successfully compiled the module.`)) 268 | 269 | // Unzip the compiled module into the Titanium shared search path. 270 | 271 | let mozuleZipPath = path.resolve(srcroot, "dist", `com.pspdfkit-iphone-${pspdfkitVersion}.zip`) 272 | let moduleDestinationPath = path.resolve(process.env.HOME, "Library", "Application Support", "Titanium") 273 | 274 | try { 275 | await spawn("unzip", ["-o", mozuleZipPath, "-d", moduleDestinationPath], spawnOptions) 276 | } catch (error) { 277 | console.error(chalk.red(`Failed to unzip the built PSPDFKit Titanium module from '${mozuleZipPath}' to '${moduleDestinationPath}'.`)) 278 | console.error(chalk.red("Please make sure this script has read-write access to the above locations.")) 279 | return false 280 | } 281 | 282 | console.log(chalk.green(`Extracted the module to '${moduleDestinationPath}'.`)) 283 | 284 | // TADA! 285 | 286 | return true 287 | 288 | } 289 | 290 | /** 291 | * Display information about required versions. 292 | * @param {VersionsCommandArgv} argv Arguments to the build command. 293 | * @returns {Promise} Value indicating whether the command succeeded. 294 | */ 295 | async function versionsCommand(argv) { 296 | 297 | // These are the shared options we will use in all command spawns. 298 | 299 | const srcroot = path.resolve(__dirname, "../") 300 | const spawnOptions = { cwd: srcroot, verbose: false } 301 | 302 | // Load the manifest file and look for `version` and `minsdk` keys in it. If 303 | // they're in there, the match variables should contain them at index 1. 304 | 305 | let manifestPath = path.resolve(srcroot, "manifest") 306 | let manifestContents = fs.readFileSync(manifestPath, "utf-8") 307 | 308 | let pspdfkitVersionMatch = manifestContents.match(/version: (.+)\n/) 309 | let titaniumVersionMatch = manifestContents.match(/minsdk: (.+)\n/) 310 | 311 | if (pspdfkitVersionMatch.length != 2 ) { 312 | console.error(chalk.red(`Could not determine required PSPDFKit version.`)) 313 | console.error(chalk.red(`Make sure 'manifest' file contains a valid value for 'version'.`)) 314 | return false 315 | } 316 | 317 | if (titaniumVersionMatch.length != 2 ) { 318 | console.error(chalk.red(`Could not determine required Titanium SDK version.`)) 319 | console.error(chalk.red(`Make sure 'manifest' file contains a valid value for 'minsdk'.`)) 320 | return false 321 | } 322 | 323 | let pspdfkitVersion = pspdfkitVersionMatch[1] 324 | let titaniumVersion = titaniumVersionMatch[1] 325 | 326 | // Parse the Podfile template since it contains the minimum iOS deployment 327 | // target. If the command fails, it means CocoaPods is not installed. 328 | 329 | let podfileTemplatePath = path.resolve(__dirname, "templates", "podfile") 330 | let podfileJsonResult; 331 | 332 | try { 333 | podfileJsonResult = await spawn("pod", ["ipc", "podfile-json", podfileTemplatePath], spawnOptions) 334 | } catch (error) { 335 | console.error(chalk.red(`CocoaPods is not installed.`)) 336 | console.error(chalk.red(`Please run \`gem install cocoapods\` first and try again.`)) 337 | return false 338 | } 339 | 340 | let iosVersion = JSON.parse(podfileJsonResult.stdout)["target_definitions"][0]["platform"]["ios"] 341 | 342 | // Respect the just option. 343 | 344 | if (_.isString(argv.just)) { 345 | if (argv.just == "pspdfkit") { 346 | console.log(pspdfkitVersion) 347 | } else if (argv.just == "titanium") { 348 | console.log(titaniumVersion) 349 | } else if (argv.just == "ios") { 350 | console.log(iosVersion) 351 | } 352 | } else { 353 | console.log(`PSPDFKit: ${pspdfkitVersion}`) 354 | console.log(`Titanium SDK: ${titaniumVersion}`) 355 | console.log(`iOS Deployment Target: ${iosVersion}`) 356 | } 357 | 358 | // TADA! 359 | 360 | return true 361 | 362 | } 363 | 364 | // Helpers. 365 | 366 | /** 367 | * Copies a file from `source` to `destination`, compiling it as a template 368 | * along the way. 369 | * @param {PathLike} source Source file location. 370 | * @param {PathLike} destination Destination file location. 371 | * @param {Object} data Template data to use. 372 | */ 373 | async function copyTemplateFile(source, destination, data) { 374 | let template = await fs.readFile(source, "utf-8") 375 | let compiled = _.template(template)(data) 376 | await fs.writeFile(destination, compiled) 377 | } 378 | 379 | /** 380 | * Extracts metadata of a dSYM file based on its file name. 381 | * @param {PathLike} path Location of the dSYM file. 382 | * @returns {DsymMetadata} Metadata of a dSYM file. 383 | */ 384 | function interpretDsym(source) { 385 | let filename = path.basename(source) 386 | let components = _.split(filename, ".") 387 | if (components.length != 4) { 388 | throw new Error(`The dSYM file name '${filename}' is invalid.`) 389 | } 390 | return { 391 | name: `${components[0]}.${components[1]}`, 392 | arch: components[2], 393 | } 394 | } 395 | 396 | /** 397 | * Reads contents of `source` directory. 398 | * @param {PathLike} source Directory location. 399 | * @param {ReadDirOptions} options Reading options. 400 | * @returns {Array} Locations of files inside `source` directory. 401 | */ 402 | function readDir(source, options) { 403 | options = _.defaults({}, options, { subdirs: false }) 404 | return fs.readdirSync(source).map(name => path.resolve(source, name)).filter(path => { 405 | if (options.subdirs) { 406 | return fs.statSync(path).isDirectory() 407 | } else { 408 | return true 409 | } 410 | }) 411 | } 412 | 413 | /** 414 | * Spawns a child process as a promise. Each value in `arguments` is shell- 415 | * escaped. If `verbose` is `true`, the full invocation and process's output is 416 | * printed, otherwise it's muted but still available in the returned value. 417 | * @param {String} command Command to spawn. 418 | * @param {Array} arguments Arguments of `command`. 419 | * @param {SpawnOptions} options Spawn options of `command`. 420 | * @returns {Promise} The spawned child process. 421 | */ 422 | async function spawn(command, arguments, options) { 423 | options = _.defaults({}, options, { verbose: false, validate: true }) 424 | if (options.verbose) { 425 | let envs = _.map(options.env, (value, key) => `${key}=${shellescape([value])}`) 426 | let invocation = _.concat(envs, [command], [shellescape(arguments)]).join(" ") 427 | console.log(`$ ${invocation}`) 428 | } 429 | let child = cp.spawn(command, arguments, { 430 | env: _.defaults({}, options.env, process.env), 431 | cwd: options.cwd || __dirname, 432 | }) 433 | if (options.verbose) { 434 | child.stdout.pipe(process.stdout) 435 | child.stderr.pipe(process.stdout) 436 | } 437 | let result = await child 438 | if (result.exitCode != 0 && options.validate) { 439 | throw new Error(`The spawned command failed with status code ${result.exitCode}.`) 440 | } else { 441 | return result 442 | } 443 | } 444 | 445 | /** 446 | * Spawns a child `which` command and checks if the given `command` exists. 447 | * @param {String} command Command to check. 448 | * @param {SpawnOptions} options Spawn options of `which` command. 449 | * @returns {Promise} Value indicating whether `command` exists. 450 | */ 451 | async function which(command, options) { 452 | options = _.extend({}, options, { validate: false }) 453 | let which = await spawn("which", [command], options) 454 | return which.exitCode == 0 455 | } 456 | 457 | // Declare custom types for JSDoc comments. 458 | 459 | /** 460 | * @typedef {{sdk?: String, verbose?: Boolean}} BuildCommandArgv 461 | * @typedef {import("child_process").ChildProcess} ChildProcess 462 | * @typedef {{name: String, arch: String}} DsymMetadata 463 | * @typedef {import("fs").PathLike} PathLike 464 | * @typedef {subdirs?: Boolean} ReadDirOptions 465 | * @typedef {{validate?: Boolean, cwd?: String, env?: NodeJS.ProcessEnv, verbose?: Boolean}} SpawnOptions 466 | * @typedef {{just?: String}} VersionsCommandArgv 467 | */ 468 | 469 | // Let Yargs handle the CLI frontend. 470 | 471 | yargs 472 | .command({ 473 | command: "build", 474 | describe: "Build the PSPDFKit Titanium module.", 475 | builder: { 476 | "sdk": { 477 | type: "string", 478 | describe: "Titanium SDK version to use.", 479 | }, 480 | "verbose": { 481 | type: "boolean", 482 | describe: "Enable additional logging.", 483 | default: false, 484 | } 485 | }, 486 | handler: buildCommand, 487 | }) 488 | .command({ 489 | command: "versions", 490 | describe: "Display information about required versions.", 491 | builder: { 492 | "just": { 493 | type: "string", 494 | describe: "Just one version.", 495 | choices: ["pspdfkit", "titanium", "ios"], 496 | }, 497 | }, 498 | handler: versionsCommand, 499 | }) 500 | .onFinishCommand((result) => { 501 | if (_.isBoolean(result) && !result) { 502 | console.error(chalk.red(`Run this command again with '--verbose' to enable additional logging.`)) 503 | process.exit(1) 504 | } 505 | }) 506 | .demandCommand() 507 | .help("help", "Show help for the given command.") 508 | .showHelpOnFail(false, "Use --help to learn available options.") 509 | .version(false) 510 | .strict() 511 | .argv 512 | -------------------------------------------------------------------------------- /scripts/templates/module.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | OTHER_LDFLAGS = <%= other_ldflags %> 11 | -------------------------------------------------------------------------------- /scripts/templates/podfile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | # 4 | # THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | # AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | # UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | # This notice may not be removed from this file. 8 | # 9 | 10 | platform :ios, "12.0" 11 | use_frameworks! 12 | 13 | install! "cocoapods", { 14 | integrate_targets: false, 15 | } 16 | 17 | target "PSPDFKit" do 18 | pod "PSPDFKit", podspec: "https://customers.pspdfkit.com/pspdfkit-ios/<%= version %>.podspec" 19 | end 20 | -------------------------------------------------------------------------------- /scripts/templates/titanium.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2021 PSPDFKit GmbH. All rights reserved. 3 | // 4 | // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY AUSTRIAN COPYRIGHT LAW 5 | // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. 6 | // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. 7 | // This notice may not be removed from this file. 8 | // 9 | 10 | TITANIUM_SDK = <%= titanium_sdkroot %> 11 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(TITANIUM_SDK)/iphone/Frameworks/**" 12 | HEADER_SEARCH_PATHS = $(inherited) "$(TITANIUM_SDK)/iphone/include" 13 | -------------------------------------------------------------------------------- /timodule.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | --------------------------------------------------------------------------------