6

I'm updating an image resizing Mac App. What I want to be able to do is if the user imports an image to be resized that has a PDFImageRep, save a new PDF file with a resolution of my choosing.

So far I've tried to draw the image at a new size, as in:

- (NSImage*)imageAtSize:(NSSize)newSize
{
    NSImage *resizedImage = [[NSImage alloc] initWithSize:newSize];

    [resizedImage lockFocus];
    [self drawInRect:NSMakeRect(0, 0, newSize.width, newSize.height)
            fromRect:NSMakeRect(0, 0, self.size.width, self.size.height)
           operation:NSCompositeSourceOver fraction:1.0];
    [resizedImage unlockFocus];

    return resizedImage;
}

but this loses the PDF image rep, and therefore makes my saving code fail.

- (void)saveAsPDFWithOutputDirectory:(NSURL *)outputDirectory size:(NSSize)newSize
{
    NSPDFImageRep *pdfRep = [self PDFImageRep];
    [pdfRep setSize:newSize];

    NSError *error = nil;
    [pdfRep.PDFRepresentation writeToURL:outputDirectory options:NSDataWritingAtomic error:&error];
    if (error)
    {
        CLS_LOG(@"Error saving image: %@", error);
    }
}

So how do I do it. The images I'm going to be using should be vector based, so is there some way I could just update a property on the PDF to specify a new resolution?

Mark Bridges
  • 8,228
  • 4
  • 50
  • 65

2 Answers2

1

Try the following approach. This creates a new PDF data object that you can save into a file.

// pdfData is a CFMutableDataRef
// auxInfo is a CFDictionary
// bounds is your new size in points!
CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData(pdfData);
CGContextRef context = CGPDFContextCreate(consumer, &bounds, auxInfo);
CGPDFContextBeginPage(context, NULL);
NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:gc];

// Your drawing code here
// You probably want to draw your NSImage here in the bounds

[NSGraphicsContext restoreGraphicsState];
CGPDFContextEndPage(context);
CGPDFContextClose(context);
CGContextRelease(context);
CGDataConsumerRelease(consumer);

Then save pdfData to file

Jacob Gorban
  • 1,431
  • 1
  • 9
  • 15
0

Thanks Jacob for supplying the correct answer. If anyone's interested the complete category I wrote with Jacob's technique is:

@implementation NSImage (MKBSaveAsPDF)

- (BOOL)isPDF
{
    return ([self PDFImageRep] != nil);
}

- (NSPDFImageRep  * _Nullable)PDFImageRep
{
    for (NSImageRep *imageRep in self.representations)
    {
        if ([imageRep isKindOfClass:[NSPDFImageRep class]]){
            return (NSPDFImageRep*)imageRep;
        }
    }

    return nil;
}

- (void)saveAsPDFWithOutputDirectory:(NSURL *)outputDirectory size:(NSSize)newSize
{
    NSPDFImageRep *pdfRep = self.PDFImageRep;

    NSMutableData *mutablePDFRef = [[NSMutableData alloc]initWithData:pdfRep.PDFRepresentation];

    CFMutableDataRef pdfDataRef = (__bridge CFMutableDataRef)(mutablePDFRef);
    CGRect bounds = CGRectMake(0, 0, newSize.width, newSize.height);

    CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData(pdfDataRef);
    CGContextRef context = CGPDFContextCreate(consumer, &bounds, nil);
    CGPDFContextBeginPage(context, NULL);
    NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
    [NSGraphicsContext saveGraphicsState];
    [NSGraphicsContext setCurrentContext:gc];

    [self drawInRect:NSMakeRect(0, 0, newSize.width, newSize.height)];

    [NSGraphicsContext restoreGraphicsState];
    CGPDFContextEndPage(context);
    CGPDFContextClose(context);
    CGContextRelease(context);
    CGDataConsumerRelease(consumer);


    NSError *error = nil;

    NSData *pdfData = (__bridge NSData *)(pdfDataRef);
    [pdfData writeToURL:outputDirectory options:NSDataWritingAtomic error:&error];
    if (error)
    {
        NSLog(@"Error saving image: %@", error);
    }
}
Mark Bridges
  • 8,228
  • 4
  • 50
  • 65