There have been plenty of requests for the ability to print from Corona SDK, but given that Corona SDK is all done with graphics in OpenGL, exactly what would you want to print? Well, one example would be to print a screenshot of your app. Another usage case would be printing downloaded PDFs.

Corona SDK doesn’t have built-in printing facilities, so how can you accomplish this? With Corona Enterprise of course! As a bonus, we’ll also demonstrate how to save an image from the system.DocumentsDirectory to the camera roll.

NOTE: This tutorial is for iOS and assumes that you have a wireless printer on the same WiFi network that supports the Apple AirPrint protocol.

Getting up to Speed

Before you proceed, you should read and understand the Corona Enterprise Quickstart (iOS) tutorial because many aspects of this tutorial build upon the lessons learned therein. Please follow all of the steps from that tutorial to set up a new project and name it whatever you prefer. This tutorial will build a plugin called image which you can use within your Corona SDK project.

Xcode Project Modifications

To begin, let’s go through the Xcode modifications which will allow this plugin to function. First, edit the AppCoronaDelegate.h file and change this line…

@interface AppCoronaDelegate : NSObject< CoronaDelegate >

…to this:

@interface AppCoronaDelegate : NSObject< CoronaDelegate, UIPrintInteractionControllerDelegate >

This will allow your app to talk to the iOS Print Interaction Controller.

Now, let’s modify the PluginLibrary.mm file. In the PluginLibrary class where you had set up definitions for the on and off functions in the previous tutorial, rename one definition to saveImage and the other to printImage. Essentially, your class should look like this:

class PluginLibrary
{
   public:
      typedef PluginLibrary Self;

   public:
      static const char kName[];
      static const char kEvent[];

   protected:
      PluginLibrary();

   public:
      bool Initialize( CoronaLuaRef listener );

   public:
      CoronaLuaRef GetListener() const { return fListener; }

   public:
      static int Open( lua_State *L );

   protected:
      static int Finalizer( lua_State *L );

   public:
      static Self *ToLibrary( lua_State *L );

   public:
      static int init ( lua_State *L );
      static int saveImage( lua_State *L );
      static int printImage( lua_State *L );

   private:
      CoronaLuaRef fListener;
};

Next, a few lines down from this, change the PluginLibrary::kName value to "plugin.image". This will let you reference the plugin with that name in your Lua code. Also change the PluginLibrary::kEvent[] value to "image":

// This corresponds to the name of the library, e.g. [Lua] require "plugin.library"
const char PluginLibrary::kName[] = "plugin.image";

// This corresponds to the event name, e.g. [Lua] event.name
const char PluginLibrary::kEvent[] = "image";

PluginLibrary::PluginLibrary()
:  fListener( NULL )
{
}

Several lines below this, in the PluginLibrary::Open method, you’ll need to update the kVTable array to represent the mapping of the Lua function to the Objective-C method:

   // Functions in library
   const luaL_Reg kVTable[] =
   {
      { "init", init },
      { "saveToAlbum", saveImage },
      { "airPrint", printImage },
      { NULL, NULL }
   };

Now, let’s write the two functions to handle printing and saving of the image. Because of the setting in the table above, "airPrint" will be the function name on the Lua side, while printImage defines the Xcode method. Since this tutorial is primarily about printing, let’s look at the printImage function first:

// [Lua] library.printImage( word )
int PluginLibrary::printImage( lua_State *L )
{
   NSString *path = [NSString stringWithUTF8String:lua_tostring( L, -1 )];
   NSData *myData = [NSData dataWithContentsOfFile: path];
    
   UIPrintInteractionController *pic = [UIPrintInteractionController sharedPrintController];
    
   if( pic && [UIPrintInteractionController canPrintData: myData] ) {
      UIPrintInfo *printInfo = [UIPrintInfo printInfo];
      printInfo.outputType = UIPrintInfoOutputGeneral;
      printInfo.jobName = [path lastPathComponent];
      printInfo.duplex = UIPrintInfoDuplexLongEdge;
      pic.printInfo = printInfo;
      pic.showsPageRange = YES;
      pic.printingItem = myData;
        
      void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) = ^(UIPrintInteractionController *pic, BOOL completed, NSError *error) {
         //self.content = nil;
         if (!completed && error) {
            NSLog(@"FAILED! due to error in domain %@ with error code %u", error.domain, error.code);
         }
      };
      [pic presentAnimated:YES completionHandler:completionHandler];
   }
   return 0;
}

This creates a new NSString object called path that is initialized with the first item passed to the Lua parameter stack. It’s expected to be a string and an error will be thrown if not. On the next line, we create an NSData object called myData initialized with the contents of the file path. The remainder of the code sets up the print interaction controller. If your app can print, we set up various parameters needed for that process. If you wish to explore the source and logic behind this code, please see here.

Now, let’s write the saveImage function. This will be named "saveToAlbum" on the Lua side, while saveImage will be the Xcode method.

// [Lua] library.saveImage( word )
int PluginLibrary::saveImage( lua_State *L )
{
   NSString *path = [NSString stringWithUTF8String:lua_tostring( L, -1 )];

   UIImage *image = [UIImage imageWithContentsOfFile:path];
   UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
 
   return 0;
}

Like the printImage function, this gets the image path passed in as the first parameter. It then generates a UIImage object based on the contents of the file at path and writes it to the camera roll.

Lua Side

Now we’ll tackle the Lua/Corona side! In your main.lua file, paste this code:

local image = require( "plugin.image" )

local printMeButton

local function handleButtonEvent( event )
   local imageName = system.pathForFile( "myimage.jpg", system.ResourceDirectory )
   image.airPrint( imageName )
   return true
end

printMeButton = widget.newButton{
   id = "button1",
   label = "Print Photo",
   labelColor = { default={ 0, 0, 0 }, over={ 0, 0, 0 } },
   onRelease = handleButtonEvent
}
printMeButton.x = display.contentCenterX
printMeButton.y = display.contentCenterY-50

image.init( listener )

Effectively, this code includes your custom plugin and creates a widget button with the label Print Photo. When the button is tapped, it calls the handleButtonEvent() function which does the magic. First, it generates a path to the image you want to print (this could also be a PDF). In this case, it picks up a file named myimage.jpg from the app’s bundle (system.ResourceDirectory). Alternatively, you could use the display.save() API to capture the screen to the system.DocumentsDirectory and pick the image from there.

With the path to that image, we call the plugin function called airPrint() (the printImage method in Objective-C).

If you wish to test the saving function, the Lua code should be almost identical, except that you’d call image.saveToAlbum( path ) instead of image.airPrint( path ).

If everything went well, you should now be able to build for a device and install it as described in the quickstart tutorial.

Conclusion

While Corona Enterprise may seem intimidating at first, utilizing it can empower your apps with great new features. Hopefully this tutorial has shown you the basic framework and will encourage you to explore new potential ideas in development.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>