How to compress a PNG image using Cocoa? - objective-c

So, this is what I need :
Take an NSImage
Compress it as much as possible (loseless compress, no obvious drop in quality)
Save it back to disk
In the past, I tried using OptiPNG - as compiled binary (and getting results asynchronously) - which worked.
However, what if I don't want a terminal-like solution with external apps, but something more Cocoa-native?
Any ideas?

NSImage* image; // your NSImage
// With compression
NSData* data = [image
TIFFRepresentationUsingCompression:NSTIFFCompressionLZW
factor:0.5f]; // you can change factor between 0 and 1.0 for preferred compression rate
if (data != nil) {
NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithData:data];
if (bitmap != nil) {
NSData* bitmapData = [bitmap
representationUsingType:NSBitmapImageFileTypePNG
properties:#{}];
if (bitmapData != nil) {
[bitmapData
writeToFile:<file_path> //path where to save png
atomically:true];
}
}
}
Update: As mentioned in comments, this does not compress png.
However I have used this code in ShareExtension and it compress received screenshots well (from 1.1-1.5MB if save in plain to 0.4-0.7MB using representationUsingType) and without quality loss, just like OP asked.

Related

How to allow forms(pdf) to be re-editable after they have been saved(Objective C)

I'm new to CoreGraphics framework.
We have used ILPDFKit library to render PDF or form
We embedded the drawn paths to existing PDF.Here is the code
-(NSData *)embededPdfAnnotationPointsInPdfAtPath:(NSString *)pdfPath
{
NSURL *pdfUrl = [NSURL fileURLWithPath:pdfPath];
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfUrl);
NSMutableData *data = [NSMutableData new];
UIGraphicsBeginPDFContextToData(data, CGRectZero, nil);
for(NSUInteger pageIndex = 1; pageIndex <= [self pdfDrawViewInfo].count; pageIndex ++)
{
// Get the current page and page frame
CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdf, pageIndex);
const CGRect pageFrame = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
UIGraphicsBeginPDFPageWithInfo(pageFrame, nil);
// Draw the page (flipped)
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -pageFrame.size.height);
CGContextDrawPDFPage(ctx, pdfPage);
CGContextRestoreGState(ctx);
UIImage *drawViewImage = [self annotatedImageForPdfPageAtIndex:(pageIndex - 1)];
UIImage *annotatedImgForPdfPage = [self imageWithImage:drawViewImage scaledToSize:pageFrame.size] ;
[annotatedImgForPdfPage drawInRect:pageFrame];
}
UIGraphicsEndPDFContext();
// CGPDFDocumentRelease(pdf);
// CGImageRelease(annotatedImgCGref);
return data;
}
Above code indicates, we are just pasting image(drawn paths) on existing PDF.
Later functionality changed as "After saving PDF with annotations(freehand drawing), we need to have control over annotated stuff"
Question:
Edit PDF with annotation(freehand drawing), save it.After saving the PDF with annotated stuff, we need to again gain its editing capability. We need to know the saving mechanism and steps of this process.Please put your views here, so that it help me a lot.
Thanks inadvance
I would probably conserve the pdf original version, and store the edits in another file.
Than each time the user save, the app apply the annotations and other stuff to original version and generate the final pdf (in case of sharing or exportation features)
That would give you a complete control, (i guess this is also the same way "apple photo", "iPhoto" and "Aperture" manage the pictures modifications...)

Writing image metadata (EXIF/TIFF/IPTC) to image file in OS X

I am creating a photo editing app, and so far I've managed to read the metadata from image files successfully (after getting an answer to this question: Reading Camera data from EXIF while opening NSImage on OS X).
source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
NSDictionary *props = (__bridge_transfer NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
This copies all the metadata of the image file to a dictionary, and it works faily well. However, I couldn't find out how to write this metadata back to a newly created NSImage (or to an image file). Here is how I save my file (where img is an NSImage instance without metadata and self.cachedMetadata is the dictionary read from the initial image):
NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]];
[rep setProperty:NSImageEXIFData withValue:self.cachedMetadata];
NSData *data;
if([[fileName lowercaseString] rangeOfString:#".png"].location != NSNotFound){
data = [rep representationUsingType:NSPNGFileType properties:nil];
}else if([[fileName lowercaseString] rangeOfString:#".tif"].location != NSNotFound){
data = [rep representationUsingType:NSTIFFFileType properties:nil];
}else{ //assume jpeg
data = [rep representationUsingType:NSJPEGFileType properties:#{NSImageCompressionFactor: [NSNumber numberWithFloat:1], NSImageEXIFData: self.cachedMetadata}];
}
[data writeToFile:fileName atomically:YES];
How can I write the metadata? I used to write just EXIF for JPEG (the dictionary was EXIF-only previously) successfully but because EXIF lacked some of the fields that the initial images had (IPTC and TIFF tags) I needed to change my reading method. Now I have all the data, but I don't know how to write it to the newly-created image file.
Thanks,
Can.
Found an answer from another StackOverflow question: How do you overwrite image metadata?:
(code taken from that question itself and modified for my needs, which contains the answer to my question)
//assuming I've already loaded the image source and read the meta
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) data, NULL);
CFStringRef imageType = CGImageSourceGetType(source);
//new empty data to write the final image data to
NSMutableData *resultData = [NSMutableData data];
CGImageDestinationRef imgDest = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)(resultData), imageType, 1, NULL);
//copy image data
CGImageDestinationAddImageFromSource(imgDest, source, 0, (__bridge CFDictionaryRef)(window.cachedMetadata));
BOOL success = CGImageDestinationFinalize(imgDest);
This worked perfectly for writing back all the metadata including EXIF, TIFF, and IPTC.
You could try either using, or looking at the code in, the "exiv2" tool maybe.

How can I extract raw data from a .tiff image in Objective-C

I have some project wherein I have to manipulate some raw data from an image generated which is in .tiff format. I only have access to the image location. Now, I want to extract some raw info from the image like number of pixels in the image, no. of bits per pixel, no. of color components, etc.
I am working on a project on MAC OS and thus, to talk to Apple APIs, Objective-C is being used.
Can anyone suggest some techniques or some Apple APIs, if possible, which can assist me in extracting the desired from the image?
P.S.: I actually preferred .tiff format since .jpeg is a lossy compression.
Maybe this code will help you a bit, NSImage already contains width and height, so you can count number of pixels.
NSImage *image = [[NSImage alloc] initWithContentsOfFile:[#"~/Desktop/image.tiff" stringByExpandingTildeInPath]];
NSData *imageData = [image TIFFRepresentation];
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)CFBridgingRetain(imageData), NULL);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, 0, NULL);
NSUInteger numberOfBitsPerPixel = CGImageGetBitsPerPixel(imageRef);
NSLog(#"Number Of Bits Per Pixel %lu", (unsigned long)numberOfBitsPerPixel);

some problems about html to pdf in iOS

Recently i'm trouble in some problems about html convert to PDF files.
I find use the followed method to convert is not quickly enough and the PDF files created are big (the file size is about 2M - 5M / 7 pages ) . And some html pages are inconsistent with the converted PDF files.
Are there any methods to control the pdf size?
Are there any methods can convert it more quickly?
Are there any methods can solve the pdf files be inconsistent with the html pages in webview ?
I would very much appreciate your help. Thank you.
- (NSData*) printToPDF
{
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData( pdfData, CGRectZero, nil );
[self prepareForDrawingPages: NSMakeRange(0, self.numberOfPages)];
CGRect bounds = UIGraphicsGetPDFContextBounds();
for ( int i = 0 ; i < self.numberOfPages ; i++ )
{
UIGraphicsBeginPDFPage();
[self drawPageAtIndex: i inRect: bounds];//
}
UIGraphicsEndPDFContext();
return pdfData;
}

NSImage acting weird

Why is this code setting artistImage to an image with 0 width and 0 height?
NSURL *artistImageURL = [NSURL URLWithString:#"http://userserve-ak.last.fm/serve/252/8581581.jpg"];
NSImage *artistImage = [[NSImage alloc] initWithContentsOfURL:artistImageURL];
As Ken wrote, the DPI is messed up in this image. If you want to force NSImage to set the real image size (ignoring the DPI), use the method described at http://borkware.com/quickies/one?topic=NSImage:
NSBitmapImageRep *rep = [[image representations] objectAtIndex: 0];
NSSize size = NSMakeSize([rep pixelsWide], [rep pixelsHigh]);
[image setSize: size];
NSImage does load this fine for me, but that particular image has corrupt metadata. Its resolution according to the exif data is 7.1999997999228071e-06 dpi.
NSImage respects the DPI info in the file, so if you try to draw the image at its natural size, you'll get something 2520000070 pixels across.
Last I checked, NSImage's -initWithContentsOfURL: only works with file URLs. You'll need to retrieve the URL first, and then use -initWithData:
It is more or less guaranteed that .representations contains NSImageRep* (of course not always NSBitmapImageRep). To be on a safe side for future extensions one can write something like code below. And it also takes into account multiple representation (like in some .icns and .tiff files).
#implementation NSImage (Extension)
- (void) makePixelSized {
NSSize max = NSZeroSize;
for (NSObject* o in self.representations) {
if ([o isKindOfClass: NSImageRep.class]) {
NSImageRep* r = (NSImageRep*)o;
if (r.pixelsWide != NSImageRepMatchesDevice && r.pixelsHigh != NSImageRepMatchesDevice) {
max.width = MAX(max.width, r.pixelsWide);
max.height = MAX(max.height, r.pixelsHigh);
}
}
}
if (max.width > 0 && max.height > 0) {
self.size = max;
}
}
#end