Skip to content

Instantly share code, notes, and snippets.

@atr000
Created February 5, 2010 04:23
Show Gist options
  • Save atr000/295508 to your computer and use it in GitHub Desktop.
Save atr000/295508 to your computer and use it in GitHub Desktop.
// A tiny program that converts PDF documents to high resolution PNG images.
//
// gcc --std=c99 -Wall -g -o pdf2png pdf2png.m -framework Cocoa
//
// Written by Evan Jones <[email protected]> Februrary, 2004
// http://www.eng.uwaterloo.ca/~ejones/
//
// Released under the "do whatever you want" public domain licence.
#include <Cocoa/Cocoa.h>
int main( int argc, char* argv[] )
{
int desiredResolution = 200; // in DPI
BOOL morePages = YES;
int page = 1;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Package all arguments as NSStrings in an NSArray
NSMutableArray* args = [NSMutableArray arrayWithCapacity: argc - 1];
for ( int i = 1; i < argc; ++ i )
{
[args addObject: [NSString stringWithCString: argv[i]] ];
}
// If we have a "--dpi" along with a corresponding argument ...
unsigned int index = NSNotFound;
if ( (index = [args indexOfObject: @"--dpi"]) != NSNotFound && index + 1 < [args count] )
{
// Parse it as an integer
desiredResolution = [[args objectAtIndex: index + 1] intValue];
[args removeObjectAtIndex: index + 1];
[args removeObjectAtIndex: index];
}
// If we have a "--page" along with a corresponding argument ...
if ( (index = [args indexOfObject: @"--page"]) != NSNotFound && index + 1 < [args count] )
{
// Parse it as an integer
page = [[args objectAtIndex: index + 1] intValue];
morePages = NO;
[args removeObjectAtIndex: index + 1];
[args removeObjectAtIndex: index];
}
if ( [args count] != 1 || [args indexOfObject: @"--help"] != NSNotFound || desiredResolution <= 0 || page <= 0 )
{
fprintf( stderr, "pdf2png [options] file\n" );
fprintf( stderr, "\t--dpi dpi\tSpecifies the resolution at which to export the pages\n" );
fprintf( stderr, "\t--page page\tSingle page to export\n" );
fprintf( stderr, "\t--help\tPrint this help message\n" );
return 1;
}
NSString* sourcePath = [args objectAtIndex: 0];
NSImage* source = [ [NSImage alloc] initWithContentsOfFile: sourcePath ];
[source setScalesWhenResized: YES];
// Tip from http://www.omnigroup.com/mailman/archive/macosx-dev/2002-February/023366.html
// Allows setCurrentPage to do anything
[source setDataRetained: YES];
if ( source == nil )
{
fprintf( stderr, "Source image '%s' could not be loaded\n", argv[1] );
return 2;
}
// The output file name
NSString* outputFileFormat = @"%@-p%01d";
// Find the PDF representation
NSPDFImageRep* pdfSource = NULL;
NSArray* reps = [source representations];
for ( int i = 0; i < [reps count] && pdfSource == NULL; ++ i )
{
if ( [[reps objectAtIndex: i] isKindOfClass: [NSPDFImageRep class]] )
{
pdfSource = [reps objectAtIndex: i];
[pdfSource setCurrentPage: page-1];
// Set the output format to have the correct number of leading zeros
unsigned int numDigits = [(NSString*) [NSString stringWithFormat: @"%d", [pdfSource pageCount]] length];
outputFileFormat = [NSString stringWithFormat: @"%%@-p%%0%dd", numDigits];
}
}
NSSize sourceSize = [source size];
// When loading a PDF document, it reports size in typographic dots. There are 72 dots per inch.
// See http://www.macosxguru.net/article.php?story=20031209091834195
// When converting, we need to scale the PDF by this factor
double sourceResolution = 72.0;
int pixels = [ [source bestRepresentationForDevice: nil] pixelsWide];
if ( pixels != 0 ) sourceResolution = ((double)pixels / sourceSize.width) * 72.0;
double scaleFactor = desiredResolution / sourceResolution;
NSSize size = NSMakeSize( sourceSize.width * scaleFactor, sourceSize.height * scaleFactor );
[NSApplication sharedApplication];
[[NSGraphicsContext currentContext] setImageInterpolation: NSImageInterpolationHigh];
[source setSize: size];
NSRect destinationRect = NSMakeRect( 0, 0, size.width, size.height );
NSImage* image = [[NSImage alloc] initWithSize:size];
[image lockFocus];
[[NSColor whiteColor] set]; // We fill the rect with white, with full opacity, later on
do
{
// Set up a temporary release pool so memory will get cleaned up properly
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSRectFillUsingOperation( destinationRect, NSCompositeClear );
[source drawInRect: destinationRect
fromRect: destinationRect
operation: NSCompositeCopy fraction: 1.0];
NSBitmapImageRep* bitmap = [ [NSBitmapImageRep alloc]
initWithFocusedViewRect: destinationRect ];
NSData* data = [bitmap representationUsingType:NSPNGFileType properties:nil];
[bitmap release];
[[NSFileManager defaultManager]
createFileAtPath: [ [NSString stringWithFormat: outputFileFormat, [sourcePath stringByDeletingPathExtension], page] stringByAppendingPathExtension: @"png"]
contents: data
attributes: nil ];
if ( morePages == YES )
{
// Go get the next page
if ( pdfSource != NULL && page < [pdfSource pageCount] )
{
[pdfSource setCurrentPage: page];
[source recache];
page ++;
}
else
{
morePages = NO;
}
}
[loopPool release];
}
while ( morePages == YES );
[image unlockFocus];
[image release];
[pool release];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment