PDF Creation in Cocoa - macos

Here with below code PDF gets created but i am able to write in PDF.But i am not able to render NSView in PDF
CGContextRef aCgPDFContextRef = [self createPDFContext:CGRectMake(0, 0, 612, 892) path:(CFStringRef)filepath];
CGContextBeginPage(aCgPDFContextRef,nil);
//Draw view into PDF
NSView *aPageNote=[[NSView alloc]initWithFrame:CGRectMake(0,0, 612, 892)];
CGAffineTransform aCgAffTrans = CGAffineTransformIdentity;
aCgAffTrans = CGAffineTransformMakeTranslation(0,892);
aCgAffTrans = CGAffineTransformScale(aCgAffTrans, 1.0, -1.0);
CGContextConcatCTM(aCgPDFContextRef, aCgAffTrans);
NSString *strText = #"Test 123333333333333333";
[strText drawAtPoint:NSMakePoint(100, 200) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSFont systemFontOfSize:40.0],NSFontAttributeName,nil]];//drawAtPoint:CGPointMake(200, 300) withFont:[NSFont systemFontOfSize:20]];
[aPageNote setWantsLayer:YES];
[aPageNote.layer renderInContext:aCgPDFContextRef];
CGContextEndPage(aCgPDFContextRef);
CGContextRelease (aCgPDFContextRef);
NSLog(#"Pdf Successfully Created");
The Method :
-(CGContextRef) createPDFContext:(CGRect)aCgRectinMediaBox path:(CFStringRef) aCfStrPath
{
CGContextRef aCgContextRefNewPDF = NULL;
CFURLRef aCfurlRefPDF;
aCfurlRefPDF = CFURLCreateWithFileSystemPath (NULL,aCfStrPath,kCFURLPOSIXPathStyle,false);
if (aCfurlRefPDF != NULL) {
aCgContextRefNewPDF = CGPDFContextCreateWithURL (aCfurlRefPDF,&aCgRectinMediaBox,NULL);
CFRelease(aCfurlRefPDF);
}
return aCgContextRefNewPDF;
}
EDIT : I am able to write in PDF using this code
CGContextBeginPage(aCgPDFContextRef,nil);
NSString *strText1 = #"I am iOS Developer";
CGContextSelectFont (aCgPDFContextRef, "Helvetica", 24, kCGEncodingMacRoman);
CGContextSetTextDrawingMode (aCgPDFContextRef, kCGTextFill);
CGContextSetRGBFillColor (aCgPDFContextRef, 0, 0, 0, 1);
const char *text1 = [strText1 UTF8String];
CGContextShowTextAtPoint (aCgPDFContextRef, 50, 375, text1, strlen(text1));
CGContextEndPage(aCgPDFContextRef);

EDIT : Render NSView and add as image into PDF like this:
//Firstly render NS element into NSImage
NSImage *i = [[NSImage alloc] initWithData:[yourView dataWithPDFInsideRect:[yourView bounds]]];
//Now u can add in PDF
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)[i TIFFRepresentation], NULL);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, 0, NULL);
CGContextDrawImage(aCgPDFContextRef,[yourView bounds], imageRef);

Related

Implicit conversion from enumeration type 'CGImageAlphaInfo'

ive updated my project to IOS 7 and now i am getting this error when resizing an image once added/taken within the app here is my code
-(UIImage *)resizeImage:(UIImage *)anImage width:(int)width height:(int)height
{
CGImageRef imageRef = [anImage CGImage];
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
if (alphaInfo == kCGImageAlphaNone)
alphaInfo = kCGImageAlphaNoneSkipLast;
CGContextRef bitmap = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(imageRef), 4 * width, CGImageGetColorSpace(imageRef), alphaInfo);
CGContextDrawImage(bitmap, CGRectMake(0, 0, width, height), imageRef);
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage *result = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap);
CGImageRelease(ref);
return result;
}
The error im getting is this
Implicit conversion from enumeration type 'CGImageAlphaInfo' (aka 'enum CGImageAlphaInfo') to different enumeration type 'CGBitmapInfo' (aka 'enum CGBitmapInfo')
I have inserted (CGBitmapInfo) before your variable alphaInfo.
Hope it solves your problem
-(UIImage *)resizeImage:(UIImage *)anImage width:(int)width height:(int)height
{
CGImageRef imageRef = [anImage CGImage];
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
if (alphaInfo == kCGImageAlphaNone)
alphaInfo = kCGImageAlphaNoneSkipLast;
CGContextRef bitmap = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(imageRef), 4 * width, CGImageGetColorSpace(imageRef), (CGBitmapInfo)alphaInfo);
CGContextDrawImage(bitmap, CGRectMake(0, 0, width, height), imageRef);
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage *result = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap);
CGImageRelease(ref);
return result;
}

Create RGB image from each channel

I have 3 files, one with only a red channel, one with only a green channel, one with only a blue channel. Now i want to combine those 3 images to one, where every image is one color-channel in the finished image.
How can i do this with cocoa? I have a solution that is working but is too slow:
NSBitmapImageRep *rRep = [[rImage representations] objectAtIndex: 0];
NSBitmapImageRep *gRep = [[gImage representations] objectAtIndex: 0];
NSBitmapImageRep *bRep = [[bImage representations] objectAtIndex: 0];
NSBitmapImageRep *finalRep = [rRep copy];
for (NSUInteger i = 0; i < [rRep pixelsWide]; i++) {
for (NSUInteger j = 0; j < [rRep pixelsHigh]; j++) {
CGFloat r = [[rRep colorAtX:i y:j] redComponent];
CGFloat g = [[gRep colorAtX:i y:j] greenComponent];
CGFloat b = [[bRep colorAtX:i y:j] blueComponent];
[finalRep setColor:[NSColor colorWithCalibratedRed:r green:g blue:b alpha:1.0] atX:i y:j];
}
}
NSData *data = [finalRep representationUsingType:NSJPEGFileType properties:[NSDictionary dictionaryWithObject:[NSNumber numberWithDouble:0.7] forKey:NSImageCompressionFactor]];
[data writeToURL:[panel URL] atomically:YES];
The Accelerate.framework provides a function to combine 3 planar images into one destination:
vImageConvert_Planar8toRGB888.
I haven't tried your approach but the vImage based method below is quite fast.
I was able to combine three (R,G,B) planes of a 1680x1050 image in ~0.1s on my Mac. The actual conversion takes ~1/3 of that time - The rest is setup & file IO.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSDate* start = [NSDate date];
NSURL* redImageURL = [[NSBundle mainBundle] URLForImageResource:#"red"];
NSURL* greenImageURL = [[NSBundle mainBundle] URLForImageResource:#"green"];
NSURL* blueImageURL = [[NSBundle mainBundle] URLForImageResource:#"blue"];
NSData* redImageData = [self newChannelDataFromImageAtURL:redImageURL];
NSData* greenImageData = [self newChannelDataFromImageAtURL:greenImageURL];
NSData* blueImageData = [self newChannelDataFromImageAtURL:blueImageURL];
//We use our "Red" image to measure the dimensions. We assume that all images & the destination have the same size
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)redImageURL, NULL);
NSDictionary* properties = (__bridge NSDictionary*)CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
CGFloat width = [properties[(id)kCGImagePropertyPixelWidth] doubleValue];
CGFloat height = [properties[(id)kCGImagePropertyPixelHeight] doubleValue];
self.image = [self newImageWithSize:CGSizeMake(width, height) fromRedChannel:redImageData greenChannel:greenImageData blueChannel:blueImageData];
NSLog(#"Combining 3 (R, G, B) planes of size %# took:%fs", NSStringFromSize(CGSizeMake(width, height)), [[NSDate date] timeIntervalSinceDate:start]);
}
- (NSImage*)newImageWithSize:(CGSize)size fromRedChannel:(NSData*)redImageData greenChannel:(NSData*)greenImageData blueChannel:(NSData*)blueImageData
{
vImage_Buffer redBuffer;
redBuffer.data = (void*)redImageData.bytes;
redBuffer.width = size.width;
redBuffer.height = size.height;
redBuffer.rowBytes = [redImageData length]/size.height;
vImage_Buffer greenBuffer;
greenBuffer.data = (void*)greenImageData.bytes;
greenBuffer.width = size.width;
greenBuffer.height = size.height;
greenBuffer.rowBytes = [greenImageData length]/size.height;
vImage_Buffer blueBuffer;
blueBuffer.data = (void*)blueImageData.bytes;
blueBuffer.width = size.width;
blueBuffer.height = size.height;
blueBuffer.rowBytes = [blueImageData length]/size.height;
size_t destinationImageBytesLength = size.width*size.height*3;
const void* destinationImageBytes = valloc(destinationImageBytesLength);
NSData* destinationImageData = [[NSData alloc] initWithBytes:destinationImageBytes length:destinationImageBytesLength];
vImage_Buffer destinationBuffer;
destinationBuffer.data = (void*)destinationImageData.bytes;
destinationBuffer.width = size.width;
destinationBuffer.height = size.height;
destinationBuffer.rowBytes = [destinationImageData length]/size.height;
vImage_Error result = vImageConvert_Planar8toRGB888(&redBuffer, &greenBuffer, &blueBuffer, &destinationBuffer, 0);
NSImage* image = nil;
if(result == kvImageNoError)
{
//TODO: If you need color matching, use an appropriate colorspace here
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)(destinationImageData));
CGImageRef finalImageRef = CGImageCreate(size.width, size.height, 8, 24, destinationBuffer.rowBytes, colorSpace, kCGBitmapByteOrder32Big|kCGImageAlphaNone, dataProvider, NULL, NO, kCGRenderingIntentDefault);
CGColorSpaceRelease(colorSpace);
CGDataProviderRelease(dataProvider);
image = [[NSImage alloc] initWithCGImage:finalImageRef size:NSMakeSize(size.width, size.height)];
CGImageRelease(finalImageRef);
}
free((void*)destinationImageBytes);
return image;
}
- (NSData*)newChannelDataFromImageAtURL:(NSURL*)imageURL
{
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)imageURL, NULL);
if(imageSource == NULL){return NULL;}
CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
CFRelease(imageSource);
if(image == NULL){return NULL;}
CGColorSpaceRef colorSpace = CGImageGetColorSpace(image);
CGFloat width = CGImageGetWidth(image);
CGFloat height = CGImageGetHeight(image);
size_t bytesPerRow = CGImageGetBytesPerRow(image);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(image);
CGContextRef bitmapContext = CGBitmapContextCreate(NULL, width, height, 8, bytesPerRow, colorSpace, bitmapInfo);
NSData* data = NULL;
if(NULL != bitmapContext)
{
CGContextDrawImage(bitmapContext, CGRectMake(0.0, 0.0, width, height), image);
CGImageRef imageRef = CGBitmapContextCreateImage(bitmapContext);
if(NULL != imageRef)
{
data = (NSData*)CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(imageRef)));
}
CGImageRelease(imageRef);
CGContextRelease(bitmapContext);
}
CGImageRelease(image);
return data;
}
Your program creates many many many many many many color objects.
Although your program could simply access the image reps' bitmapData, it would require your program to know a lot about bitmap representations.
Before taking that approach, you should prefer to let Quartz do the heavy lifting by rendering each image to a CGBitmapContext (e.g. using CGContextDrawImage(gtx, rect, img.CGImage)) and then extracting/copying the rendered component values from the rendered result over to a destination RGB bitmap.
If your inputs are not multicomponent color models (e.g. grayscale), then you should render to the source color model to save a bunch of CPU time and memory.

Unable to call a method

I am trying to call the method below using [self changeColor]; and getting
"No visible interface for #MyClassViewController declares the selector "changeColor"
I did declare it like that on both .h and .m:
#interface changeColor
- (UIImage *) changeColor:(UIImage *)image;
#end
What I am doing wrong?
Please help
-------------Code-------------
- (UIImage *) changeColor:(UIImage *)image
{
UIGraphicsBeginImageContext(image.size);
CGRect contextRect;
contextRect.origin.x = 0.0f;
contextRect.origin.y = 0.0f;
contextRect.size = [image size];
// Retrieve source image and begin image context
CGSize itemImageSize = [image size];
CGPoint itemImagePosition;
itemImagePosition.x = ceilf((contextRect.size.width - itemImageSize.width) / 2);
itemImagePosition.y = ceilf((contextRect.size.height - itemImageSize.height) );
UIGraphicsBeginImageContext(contextRect.size);
CGContextRef c = UIGraphicsGetCurrentContext();
// Setup shadow
// Setup transparency layer and clip to mask
CGContextBeginTransparencyLayer(c, NULL);
CGContextScaleCTM(c, 1.0, -1.0);
CGContextClipToMask(c, CGRectMake(itemImagePosition.x, -itemImagePosition.y, itemImageSize.width, -itemImageSize.height), [image CGImage]);
CGContextSetRGBFillColor(c, 0, 0, 1, 1);
NSLog(#"--------- METHOD DETECTED");//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST
/*
switch (row)
{
case 0:
CGContextSetRGBFillColor(c, 0, 0, 1, 1);
break;
default:
CGContextSetRGBFillColor(c, 1, 0, 0., 1);
break;
}
*/
contextRect.size.height = -contextRect.size.height;
contextRect.size.height -= 15;
// Fill and end the transparency layer
CGContextFillRect(c, contextRect);
CGContextEndTransparencyLayer(c);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
The problem is [self changeColor];. changeColor and changeColor: are two different things. If you define it as taking one parameter, you have to call it that way.

can we save the contents drawn on the pdf as a new pdf ? xcode

Can we save the contents drawn over the pdf , as pdf again , i tried to save the pdf with the contents but with no luck , it saves with blank pdf
the code:
- (void)drawRect:(CGRect)rect
{
NSString *pathToPdfDoc = [[NSBundle mainBundle] pathForResource:#"myPDF" ofType:#"pdf"];
NSURL *pdfUrl = [NSURL fileURLWithPath:pathToPdfDoc];
document = CGPDFDocumentCreateWithURL((__bridge CFURLRef)pdfUrl);
size_t count = CGPDFDocumentGetNumberOfPages (document);// 3
if (count == 0)
{
NSLog(#"PDF needs at least one page");
return;
}
CGRect paperSize = CGRectMake(0.0,0.0,595.28,841.89);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
// flip context so page is right way up
CGContextTranslateCTM(currentContext, 0, paperSize.size.height);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGPDFPageRef page = CGPDFDocumentGetPage (document, 1); // grab page 1 of the PDF
CGContextDrawPDFPage (currentContext, page); // draw page 1 into graphics context
// flip context so annotations are right way up
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGContextTranslateCTM(currentContext, 0, -paperSize.size.height);
////////////////////////////////
[curImage drawAtPoint:CGPointMake(0, 0)];
CGPoint mid1 = midPoint(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
CGContextRef context = UIGraphicsGetCurrentContext();
[self.layer renderInContext:context];
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
CGContextStrokePath(context);
[super drawRect:rect];
// get a temprorary filename for this PDF
NSString* path = [[NSBundle mainBundle] pathForResource:#"sampleData" ofType:#"plist"];
path = NSTemporaryDirectory();
self.pdfFilePath = [path stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.pdf", [[NSDate date] timeIntervalSince1970] ]];
UIGraphicsBeginPDFContextToFile(#"test.pdf", paperSize, nil);
[#"annotation" drawInRect:CGRectMake(100.0, 100.0, 200.0, 40.0) withFont:[UIFont systemFontOfSize:18.0]];
}
And I tried to close the PDF on button click
- (IBAction)buttonPressed:(UIButton *)button {
UIGraphicsEndPDFContext();
return;
}
But it's not working.
Any help will be greatly appreciated , thanks in advance
figured it out , here is the function to create the pdf with the contents drawn or annotated on it
-(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();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[aView.layer renderInContext:pdfContext];
// 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: %#",documentDirectory);
}
and called the function on button click .

Using CALayer's renderInContext: method with geometryFlipped

I have a CALayer (containerLayer) that I'm looking to convert to a NSBitmapImageRep before saving the data out as a flat file. containerLayer has its geometryFlipped property set to YES, and this seems to be causing issues. The PNG file that is ultimately generated renders the content correctly, but doesn't seem to takes the flipped geometry into account. I'm obviously looking for test.png to accurately represent the content shown to the left.
Attached below is a screenshot of the problem and the code I'm working with.
- (NSBitmapImageRep *)exportToImageRep
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
int bitmapByteCount;
int bitmapBytesPerRow;
int pixelsHigh = (int)[[self containerLayer] bounds].size.height;
int pixelsWide = (int)[[self containerLayer] bounds].size.width;
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
context = CGBitmapContextCreate (NULL,
pixelsWide,
pixelsHigh,
8,
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
if (context == NULL)
{
NSLog(#"Failed to create context.");
return nil;
}
CGColorSpaceRelease(colorSpace);
[[[self containerLayer] presentationLayer] renderInContext:context];
CGImageRef img = CGBitmapContextCreateImage(context);
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:img];
CFRelease(img);
return bitmap;
}
For reference, here's the code that actually saves out the generated NSBitmapImageRep:
NSData *imageData = [imageRep representationUsingType:NSPNGFileType properties:nil];
[imageData writeToFile:#"test.png" atomically:NO];
You need to flip the destination context BEFORE you render into it.
Update your code with this, I have just solved the same problem:
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, pixelsHigh);
CGContextConcatCTM(context, flipVertical);
[[[self containerLayer] presentationLayer] renderInContext:context];

Resources