Cannot create PDF document with 400+ pages on iOS
I am using the following pseudocode to generate a PDF document:
CGContextRef context = CGPDFContextCreateWithURL(url, &rect, NULL);
for (int i = 1; i <= N; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CGContextBeginPage(context, &mediaBox);
// drawing code
CGContextEndPage(context);
[pool release];
}
CGContextRelease(context);
It works very well with small documents (N < 100
pages), but it uses too much
memory and crashes if the document has more than about 400 pages (it开发者_运维百科 received
two memory warnings before crashing.) I have made sure there were no leaks using
Instruments. What's your advice on creating large PDF documents on iOS? Thanks a lot.
edit: The pdf creation is done in a background thread.
Since you're creating a single document via CGPDFContextCreateWithURL
the entire thing has to be held in memory and appended to, something that commonly (though I can't say for certain with iOS and CGPDFContextCreateWithURL
) requires a full before and after copy of the document to be kept. No need for a leak to create a problem, even without the before-and-after issue.
If you aren't trying to capture a bunch of existing UIKit-drawn stuff -- and in your sample it seems that you're not -- use the OS's printing methods instead, which offer built-in support for printing to a PDF. UIGraphicsBeginPDFContextToFile
writes the pages out to disk as they're added so the whole thing doesn't have to be held in memory at once. You should be able to generate a huge PDF that way.
Probably not the answer you want to hear, but looking at it from another perspective.
Could you consider it as a limitation of the device?... First check the number of pages in the PDF and if it is too large, give a warning to the user. Therefore handling it gracefully.
You could then expand on this....
You could construct small PDF's on the iDevice and if the PDF is too large, construct it server-side the next time the iDevice has a net connection.
If you allocate too much memory, your app will crash. Why is generating an unusually large PDF a goal? What are you actually trying to accomplish?
What about using a memory mapped file to back your CG data consumer? Then it doesn't necessarily have to fit in RAM all at once.
I created an example here: https://gist.github.com/3748250
Use it like this:
NSURL * url = [ NSURL fileURLWithPath:@"pdf.pdf"] ;
MemoryMappedDataConsumer * consumer = [ [ MemoryMappedDataConsumer alloc ] initWithURL:url ] ;
CGDataConsumerRef cgDataConsumer = [ consumer CGDataConsumer ] ;
CGContextRef c = CGPDFContextCreate( cgDataConsumer, NULL, NULL ) ;
CGDataConsumerRelease( cgDataConsumer ) ;
// write your PDF to context `c`
CGPDFContextClose( c ) ;
CGContextRelease( c ) ;
return 0;
精彩评论