📜 ⬆️ ⬇️

Developing Titanium module for iOS

In this article I will describe the creation of a native iOS module for Titanium . The purpose of the article is to show basic things when creating a Titanium module, so that if necessary you are not afraid to expand / speed up the basic functionality of Titanium.SDK. The main task of the module will be to save a pdf file with overlapping images on top of the pages.

Who just needs such a module - the source code .

Titanium allows you to create mobile applications in javascript. But unlike phonegap, it does not just wrap the html5 application in the WebView, but runs the nodeJS server on which it all runs. All this is good, but with pdf JavaScript is not good friends. For rendering, there is pdf.js in which everything is not smooth on a mobile safari, there is jspdf for generation, in addition to poor documentation of memory problems. The fact is that a large file is generated in RAM and then only saved, but often the OS kills the application before the file is saved.
So, under the cut is a relatively simple way to speed up the application.

It is assumed the use of Titanium SDK is not lower than the 3rd version. After installing titanium on your MacOS, the command “titanium” will be available in the console, this is not what we need. And we need "titanium.py" from "~ / Library / Application \ Support / Titanium / mobilesdk / osx / [SDK Version] /". Add aliases if not already done.
')
$ cat ~/.bash_profile alias ios_builder="/Users/peinguin/Library/Application\ Support/Titanium/mobilesdk/osx/3.1.3.GA/iphone/builder.py" alias titanium.py="/Users/peinguin/Library/Application\ Support/Titanium/mobilesdk/osx/3.1.3.GA/titanium.py" 

After that you can create a skeleton module.

 $ titanium.py create --platform=iphone --type=module --dir=/Volumes/yanpix_projects --name=pdfsaver --id=ti.pdfsaver 

You can assemble the module with the command:

 $ ./build.py 

But for now there is nothing to collect. For starters, it is best to outline how you will use the module. In my case, I had the original pdf document and the canvas for each of its marked pages. In some cases, I also needed the thumbnail of the first page of the pdf document.

After you decide on the required functionality, describe it in “example / app.js”.

 // TODO: write your module tests here var pdfsaver = require('ti.pdfsaver'); Ti.API.info("module is => " + pdfsaver); var old = Titanium.Filesystem.getFile(Titanium.Filesystem.getTempDirectory(),'test.pdf'); var newpdf = Titanium.Filesystem.getFile(Titanium.Filesystem.getTempDirectory(),'export.pdf'); pdfsaver.saveInExportFileWithDrawings( old.resolve(), newpdf.resolve(), { 1: 'data:image/png;base64,[base64 image representation], 4: 'data:image/png;base64,[base64 image representation]' }, 1 ); var jpeg = Titanium.Filesystem.getFile(Titanium.Filesystem.getTempDirectory(),'export.jpeg'); pdfsaver.saveThumbnail( newpdf.resolve(), jpeg.resolve() ); 


If you can get the “Resource directory” in the application, then in this case it is better to just drop the files in the application's “Temp directory” (~ / Library / Application Support / iPhone Simulator / 5.1 / Applications / [app ID] / tmp).

If you believe the documentation, then there is no point in changing “TiPdfsaverModuleAssets.h” and “TiPdfsaverModuleAssets.m” - they are frayed. Own code should be written in “TiPdfsaverModule.m” and, accordingly, “TiPdfsaverModule.h”. Here is the code of my functions:

 #pragma Public APIs -(void)saveThumbnail:(id)args{ NSString *pdf = [args objectAtIndex:0]; NSString *jpeg = [args objectAtIndex:1]; CFURLRef url = CFURLCreateWithFileSystemPath (NULL, (CFStringRef)pdf, kCFURLPOSIXPathStyle, 0); CGPDFDocumentRef templateDocument = CGPDFDocumentCreateWithURL(url); CFRelease(url); CGPDFPageRef templatePage = CGPDFDocumentGetPage(templateDocument, 1); // get the first page CGRect templatePageBounds = CGPDFPageGetBoxRect(templatePage, kCGPDFCropBox); UIGraphicsBeginImageContext(templatePageBounds.size); CGContextRef contextRef = UIGraphicsGetCurrentContext(); CGContextTranslateCTM(contextRef, 0.0, templatePageBounds.size.height); CGContextScaleCTM(contextRef, 1.0, -1.0); CGContextDrawPDFPage(contextRef, templatePage); UIImage *imageToReturn = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); CGPDFDocumentRelease(templateDocument); [UIImageJPEGRepresentation(imageToReturn, 1.0) writeToFile:jpeg atomically:YES]; } -(void)saveInExportFileWithDrawings:(id)args{ NSString *fresh = [args objectAtIndex:0]; NSString *exportpath = [args objectAtIndex:1]; NSDictionary *drawings = [args objectAtIndex:2]; NSNumber *all = [args objectAtIndex:3]; CFURLRef url = CFURLCreateWithFileSystemPath (NULL, (CFStringRef)fresh, kCFURLPOSIXPathStyle, 0); CGPDFDocumentRef templateDocument = CGPDFDocumentCreateWithURL(url); CFRelease(url); size_t count = CGPDFDocumentGetNumberOfPages(templateDocument); UIGraphicsBeginPDFContextToFile(exportpath, CGRectMake(0, 0, 612, 792), nil); for (int pageNumber = 1; pageNumber <= count; pageNumber++) { id image = [drawings objectForKey:[NSString stringWithFormat:@"%d",pageNumber ]]; if(image == nil && [all boolValue] == NO){ continue; } CGPDFPageRef templatePage = CGPDFDocumentGetPage(templateDocument, pageNumber); CGRect templatePageBounds = CGPDFPageGetBoxRect(templatePage, kCGPDFCropBox); UIGraphicsBeginPDFPageWithInfo(templatePageBounds, nil); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextTranslateCTM(context, 0.0, templatePageBounds.size.height); CGContextScaleCTM(context, 1.0, -1.0); CGContextDrawPDFPage(context, templatePage); CGContextTranslateCTM(context, 0.0, templatePageBounds.size.height); CGContextScaleCTM(context, 1.0, -1.0); if(image != nil){ NSURL *url = [NSURL URLWithString:image]; NSData *imageData = [NSData dataWithContentsOfURL:url]; UIImage *ret = [UIImage imageWithData:imageData]; [ret drawInRect:CGRectMake(0, 0, templatePageBounds.size.width, templatePageBounds.size.height)]; } } CGPDFDocumentRelease(templateDocument); UIGraphicsEndPDFContext(); } 

You can pass as many arguments as you like in functions and get values ​​through objectAtIndex. Or check for an index. All parameters are passed as objects by reference.

Now to check the correct operation of the module you need to perform:

 $ titanium.py run 

When you make sure everything is working properly. Build via “build.py” and unzip the zip file, which is in the root of the module, to the root of your project. And also add the “module” tag to the “modules” tag in the project's “tiapp.xml” file.

 <modules> <module platform="iphone">ti.pdfsaver</module> </modules> 

The article describes only the basic things. Not a word about View and Proxy. But this is enough to not fall into depression, when the customer asks for something that will slow down a lot on JS, but it’s already possible to rewrite the application from scratch. On JS, you can quickly develop the application itself, catch the exception, but for the application itself to work quickly, you should still use the native code. I think over time, all the code of my application will be rewritten to objective-c which will, in the end, give the opportunity to completely abandon Titanium.

Source: https://habr.com/ru/post/201354/


All Articles