-
-
Save iosandroiddev/4167697 to your computer and use it in GitHub Desktop.
memory mapped core graphics data consumer for writing PDFs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#import <CoreGraphics/CoreGraphics.h> | |
@interface MemoryMappedDataConsumer : NSObject | |
@property ( nonatomic, readonly ) size_t size ; | |
@property ( nonatomic, readonly ) size_t capacity ; | |
@property ( nonatomic, readonly ) CGDataConsumerRef CGDataConsumer ; | |
@property ( nonatomic, readonly, copy ) NSURL * url ; | |
-(id)initWithURL:(NSURL*)url ; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "MemoryMappedDataConsumer.h" | |
@interface MemoryMappedDataConsumer () | |
// private | |
@property ( nonatomic ) size_t size ; | |
@property ( nonatomic ) size_t capacity ; | |
@property ( nonatomic, strong, readonly ) NSFileHandle * fileHandle ; | |
@property ( nonatomic ) void * buffer ; | |
@property ( nonatomic, copy ) NSURL * url ; | |
-(size_t)putBytes:(const void *)bytes count:(size_t)count ; | |
@end | |
@implementation MemoryMappedDataConsumer | |
@synthesize CGDataConsumer = _CGDataConsumer ; | |
@synthesize fileHandle = _fileHandle ; | |
static size_t DataConsumerPutBytesCallback( MemoryMappedDataConsumer * consumer, const void *buffer, size_t count) | |
{ | |
return [ consumer putBytes:buffer count:count ] ; | |
} | |
static void DataConsumerReleaseConsumer( MemoryMappedDataConsumer * consumer ) | |
{ | |
CFRelease((__bridge CFTypeRef)consumer); | |
} | |
-(id)initWithURL:(NSURL*)url | |
{ | |
if (( self = [ super init ] )) | |
{ | |
self.url = url ; | |
} | |
return self ; | |
} | |
-(CGDataConsumerRef)CGDataConsumer | |
{ | |
if ( !_CGDataConsumer ) | |
{ | |
_CGDataConsumer = CGDataConsumerCreate( (__bridge_retained void *)self, &(CGDataConsumerCallbacks){ | |
.putBytes = (CGDataConsumerPutBytesCallback)DataConsumerPutBytesCallback, | |
.releaseConsumer = (CGDataConsumerReleaseInfoCallback)DataConsumerReleaseConsumer | |
}); | |
} | |
return _CGDataConsumer ; | |
} | |
-(size_t)putBytes:(const void *)bytes count:(size_t)count | |
{ | |
size_t newSize = self.size + count ; | |
if ( newSize > self.capacity ) | |
{ | |
size_t newCapacity = self.capacity ; | |
newCapacity += newCapacity >> 1 ; | |
newCapacity = round_page( MAX( newCapacity, MAX( 4 * getpagesize(), count ) ) ) ; | |
[ self.fileHandle truncateFileAtOffset:newCapacity ] ; | |
if ( self.buffer && self.capacity ) | |
{ | |
munmap( self.buffer, self.capacity ) ; | |
} | |
void * newBuffer = mmap( self.buffer | |
, newCapacity | |
, PROT_WRITE | PROT_READ | |
, MAP_SHARED | |
, self.fileHandle.fileDescriptor | |
, 0 ) ; | |
if ( !newBuffer || newBuffer == (void*)~0l ) // mmap returns (-1) on error | |
{ | |
// couldn't expand buffer, bail: | |
return 0 ; | |
} | |
self.buffer = newBuffer ; | |
self.capacity = newCapacity ; | |
} | |
bcopy( bytes, self.buffer + self.size, count ) ; | |
self.size = newSize ; | |
return count ; | |
} | |
-(NSFileHandle *)fileHandle | |
{ | |
if ( !_fileHandle ) | |
{ | |
NSError * error = nil ; | |
NSFileManager * fileManager = [ NSFileManager defaultManager ] ; | |
if ( ![ fileManager fileExistsAtPath:self.url.path ] ) | |
{ | |
BOOL success = [ fileManager createFileAtPath:self.url.path contents:nil attributes:nil ] ; | |
if ( !success ) | |
{ | |
@throw @"Couldn't create backing store file" ; | |
} | |
} | |
NSFileHandle * fileHandle = [ NSFileHandle fileHandleForUpdatingURL:self.url error:&error ] ; | |
if ( !fileHandle ) | |
{ | |
if ( error ) { @throw error ; } | |
} | |
_fileHandle = fileHandle ; | |
} | |
return _fileHandle ; | |
} | |
@end | |
// mediaBox and auxInfoDict can be NULL | |
CGContextRef CreateMemoryMappedPDFContext( NSURL * fileURL, const CGRect * mediaBox, CFDictionaryRef auxInfoDict ) | |
{ | |
MemoryMappedDataConsumer * consumer = [ [ MemoryMappedDataConsumer alloc ] initWithURL:fileURL ] ; | |
CGContextRef c = CGPDFContextCreate( [ consumer CGDataConsumer ], mediaBox, auxInfoDict ) ; | |
return c ; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment