PDF writing from iPad - objective-c

I am able to generate a PDF with the following code. Can someone please help me with how I would add text to this? Thanks in advance.
-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
// Creates a mutable data object for updating with binary data, like a byte array
NSMutableData *pdfData = [NSMutableData data];
// Points the pdf converter to the mutable data object and to the UIView to be converted
UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
UIGraphicsBeginPDFPage();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[aView drawRect:aView.bounds];
// remove PDF rendering context
UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
}

I've never done this (yet, I plan getting on it tomorrow). But I think the right place to go is the Quartz 2D programming guide. It's also available from your developer documentation if you need it offline.
Basically you the same as you posted but appart from calling drawRect: you perform all the text drawing you want on that context.
There's useful information on writing simple strings on this guide
I hope it helps. It sure helped me!

Related

Objective-C/Cocoa to JXA

I need to convert a PNG file to Base64 data so that I can add it to a JSON object, using JXA (JavaScript Application Scripting).
JXA is limited compared to regular JavaScript so I can't immediately use functions from FileReader, etc.
From what I've read, there is no way that I know how to do this without using Objective-C/Cocoa (which I only started reading about today for this task).
I found the following code in another post:
NSArray *keys = [NSArray arrayWithObject:#"NSImageCompressionFactor"];
NSArray *objects = [NSArray arrayWithObject:#"1.0"];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
NSImage *image = [[NSImage alloc] initWithContentsOfFile:[imageField stringValue]];
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithData:[image TIFFRepresentation]];
NSData *tiff_data = [imageRep representationUsingType:NSPNGFileType properties:dictionary];
NSString *base64 = [tiff_data encodeBase64WithNewlines:NO];
I believe it is pertinent to what I am trying to do- does anybody know how I can bridge this method to use it in JXA?
I have been reading over the JXA Cookbook's section on Syntax for calling ObjC functions, but I am having difficulty understanding it... this is all that I have come up with so far:
var desktopString = app.pathTo("desktop").toString()
var file = `${desktopString}/test.png`
ObjC.import("Cocoa");
var image = $.NSImage.alloc.initWithContentsOfFile(file)
var imageRep = $.NSBitmapImageRep.alloc.initWithData(image)
But I don't know how to proceed- I am thrown off by:
The whole initial NSArray/NSDictionary part
TIFFRepresentation (Do I need it? Where do I put it?)
NSData *tiff_data = [imageRep representationUsingType:NSPNGFileType
properties:dictionary]; (There's no alloc! Why is dictionary needed?)
NSString *base64 = [tiff_data encodeBase64WithNewlines:NO]; (Again,
no alloc.)
I would be very appreciative if somebody could point me in the right direction / give me a few pointers on how I can accomplish what I am trying to do.
Thank you in advance!
Converting an image file into an NSImage representation and then onto a base-64 string is a lot of work, and was only applicable to the answer you sourced because the OP there was coming from a starting point of having NSImage class data. As you stated that you have a .png file, the route is much simpler:
ObjC.import('Foundation');
function fileToBase64(filepath) {
const standardizedPath = $.NSString.stringWithString(filepath)
.stringByStandardizingPath;
const base64String = $.NSData.dataWithContentsOfFile(standardizedPath)
.base64EncodedStringWithOptions(0);
return ObjC.unwrap(base64String);
}
(() => {
return fileToBase64('~/Desktop/test.png');
})();
For reference, this returns identical output to the following bash shell command:
base64 --input ~/Desktop/test.png
PS. For the benefit of learning, despite what the JXA Cookbook teaches, try not to import the entire Cocoa framework into your scripts, and just import the ones specific to the objective-c classes you're using.

Storing an array of CGLayer to file

I'm working on an OSX application where I need to store 446 CGLayers that get placed in a PDF context and am wondering if there's a way to write and read them from a file, rather than generating them when the application loads
I've read that CGLayer is no longer recommended, but I feel they really fit what I need. Also, if I use bitmapGraphicsContexts, they can pixelate when zooming in.
I am able to store them in NSArrays, both by storing them in NSValue and puting them into the array by bridging. I've also tried storing them in C arrays, but that didn't work out.
My problem comes when trying to store these arrays in a file. writeToFile: doesn't work with CGLayers, but NSKeyedArchiver/NSKeyedUnarchiver hasn't worked either, both when the layers are in NSValues or bridged.
Here's my method that attempts to write and read an array containing a single layer from a file.
+(CGLayerRef) colorAnnotations:(CGContextRef)context{
float symbolSize = 8;
CGRect glyphBox = CGRectMake(0,0, 8, 8);
CGLayerRef annotationLayer = CGLayerCreateWithContext (context,glyphBox.size, NULL);
CGContextRef annotaionLayerContext = CGLayerGetContext(annotationLayer);
CGMutablePathRef annot = CGPathCreateMutable();
//Drawing annotation
/*...*/
NSMutableArray *test = [[NSMutableArray alloc]init];
[test addObject:[[NSValue alloc] initWithBytes:&annotationLayer objCType:#encode(CGLayerRef)]];
//Getting file path in Documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:#"/annots.data"];
[NSKeyedArchiver archiveRootObject:test toFile:dataPath];
NSMutableArray *testLoads = [NSKeyedUnarchiver unarchiveObjectWithFile:dataPath];
CGLayerRef layerToReturn;
[[testLoads objectAtIndex:0]getValue:&layerToReturn];
return layerToReturn;
}
I get [NSKeyedArchiver encodeValueOfObjCType:at:]: unknown type encoding ('^')') was raisedfrom this, pretty sure because of the CGLayerRef type.
The lines needed to draw the different annotations I need are pretty long, so I've been trying to figure out a way to have them made and stored in a file without having to make them on startup each time. So far I'm not seeing a way to do this, but was hoping someone here may know of one and would appreciate any help.

unable to save an NSMutableArray of image using persistent storage

I am trying to store an NSMutableArray of images using persistive storage but it is not storing it
here is my code
[imagingList addObject:image ]; //image is of type UIImage
imagingHistory=[NSUserDefaults standardUserDefaults];
[imagingHistory setObject:imagingList forKey:#"imaging history" ];
and in my view did load I write
imagingList = [[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"imaging history" ];
when I re-load the application the list of objects is set back to Zero
am I missing something ?
You can't save a UIImage directly in the user defaults. You have to convert it to an NSData first. Just by chance, when I was looking for the NSUserDefaults reference page to answer this question, a found a tutorial on how to do exactly what you want.
Hmm you really shouldn't be trying to save UIImages into NSUserDefaults. Take a look at this resource https://discussions.apple.com/thread/1729849?start=0&tstart=0 it explains how to get the NSData of the image and write it to a file. From there you can save a reference (NSURL or NSString) to the image's path in your app's document directory and you can load it from there using something like UIImage aImage = [UIImage imageWithData:[NSData datWithContentsOfURL:pathURL]].
Hope that helps.

Writing text to a PDF via from an NSString

I am fairly new to iOS development and this one is killing me. I have not found one complete (simple) example of how to write text to a pdf on the iPhone. The Apple documentation on the subject is code snippets and difficult to follow (for me anyway), and having downloaded the Quartz demo I discovered that all it did was display a pdf that already existed.
Simply put I have the world's simplest view-based app at this point. The view has one button in it. When the button is pressed an NSString is built that contains the numbers 1 through 15. I am able to write a text file to the device that contains the contents of the NSString, but would much prefer it be a pdf.
The bottom line is that I want to build a pdf file from the NSString, ultimately to be emailed as an attachment.
If anyone can point me somewhere that has a complete project that writes text to a pdf I would sure appreciate it.
// Create URL for PDF file
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filename = #"test.pdf";
NSURL *fileURL = [NSURL fileURLWithPathComponents:[NSArray arrayWithObjects:documentsDirectory, filename, nil]];
// Create PDF context
CGContextRef pdfContext = CGPDFContextCreateWithURL((CFURLRef)fileURL, NULL, NULL);
CGPDFContextBeginPage(pdfContext, NULL);
UIGraphicsPushContext(pdfContext);
// Flip coordinate system
CGRect bounds = CGContextGetClipBoundingBox(pdfContext);
CGContextScaleCTM(pdfContext, 1.0, -1.0);
CGContextTranslateCTM(pdfContext, 0.0, -bounds.size.height);
// Drawing commands
[#"Hello World!" drawAtPoint:CGPointMake(100, 100) withFont:[UIFont boldSystemFontOfSize:72.0f]];
// Clean up
UIGraphicsPopContext();
CGPDFContextEndPage(pdfContext);
CGPDFContextClose(pdfContext);

Why does my data successfully write to a file on the iPhone simulator, but not my device?

I have the following code that saves the users sketch data to a file...
//Auto Save the sketch
NSString *filename = [NSString stringWithFormat:#"%#.png", sketchID];
CGImageRef imageRef = CGBitmapContextCreateImage(paintView.canvas.mBitmapContext);
UIImage* image = [[UIImage alloc] initWithCGImage:imageRef];
NSData* imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:filename atomically:YES];
CGImageRelease(imageRef);
[image release];
the SketchID is a unique alphanumeric value, so an example of the filename I'm saving to is "p1.png". The code I use to read the file is...
//Load the sketch where the user left off, if there is one
if(fileName != nil)
{
UIImage* image = [UIImage imageWithContentsOfFile:fileName];
if(image != nil)
{
.
.
This code seems to work fine when running on the simulator, but when I run it on the device, the fails to load the image. I new to iOS development and I'm still learning how files are stored. My questions are...
Why would saving/loading the file work on the simulator, but not the device?
When I create the filename that I want to save the data to, do I need to also include a path to a specific directory on the iPhone where the data should be properly stored? Or should "p1.png" work fine when I call the writeToFile: method? And what about the imageWithContentsOfFile: method?
Why would saving/loading the file work
on the simulator, but not the device?
There are tons of reasons, but the most common is trying to write to a location that isn't writable on the device, and the most common reason for that is writing to the application bundle itself.
All iPhone apps are "sandboxed" on the device, meaning they cannot access the filesystem outside of the directory the application is installed into.
When I create the filename that I want
to save the data to, do I need to also
include a path to a specific directory
on the iPhone where the data should be
properly stored? Or should "p1.png"
work fine when I call the writeToFile:
method? And what about the
imageWithContentsOfFile: method?
You can write to your application's temp directory (if you just need a temporarily place to put something during a run) or the Documents directory (if you need to store something more permanently, and also have it backed up by iTunes).
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
I found that the simulator is very forgiving in writing files but the devices of course are not. My problem was that I was creating filePath incorrectly that the simulator did not catch. Simulator should have caught this one.
Here was my wrong and correct swift code
docsDir!+"filename.dat" // wrong does not have path separator between dir and file
docsDir!.stringByAppendingPathComponent("filename.dat") // works since it puts the "/" in between the two.