Re: Report printing
Re: Report printing
- Subject: Re: Report printing
- From: "Louis C. Sacha" <email@hidden>
- Date: Sun, 30 Nov 2003 06:20:15 -0800
Hello...
Well, I didn't run into that problem for my implementation, because I
was caching the PDF data outside the drawRect: method, mainly to
speed up redrawing since the display involved a bunch of calculations
on a large set of data. So I didn't have problems printing, because
the PDF data was already cached (similar to the case where you
created the pdf data in your initializer).
I did a bit of digging, and it appears the reason that it is failing
for you when you try to make the PDF inside the drawRect: method is
that there can only be one NSPrintOperation per thread. You already
have one print operation running to print the whole printView, and
the dataWithPDFInsideRect: method for the recordView uses a print
operation to generate the PDF data (which fails). I figured this out
from the documentation for NSPrintOperation in the AppKit
documentation, based on the information about the class method
PDFOperationWithView:insideRect:toData: which talks about raising an
exception if there is already a
print operation in progress.
So there are two possible workarounds:
1) Similar to what you mentioned, create the pdfData for all of your
records during the initialization of printView and store them in an
NSArray that you add as an instance variable. Then during drawRect:
access the records as needed out of the NSArray. Depending on how
many records might be printed at one time, this might be the safest
approach, but for large numbers of records it might result in memory
problems and have speed issues.
2) Use multithreading to get around the one print operation per thread limit...
(Note: I created a test case to verify the original problem and make
sure this solution actually works, but I haven't done any rigorous
testing... so user beware. Hopefully other list members will point
out any problems that might remain. This was tested on 10.2.6.)
To do this, you would need to add one instance variable and the
following methods (or something similar) to your RecordView class:
@interface RecordView : NSView
{
...
volatile BOOL doneRendering;
}
...
@end
@implementation RecordView
...
- (NSData *)dataWithPDFInsideRect:(NSRect)rect
/* This method overrides the default NSView version to allow the view
to be rendered to PDF within another print operation. If there is
already a print operation running on the current thread, this method
spawns a new thread to render the PDF, otherwise the default NSView
version of dataWithPDFInsideRect: is called... */
{
if ([NSPrintOperation currentOperation])
{
NSMutableData *renderedData = [[NSMutableData alloc] init];
NSMutableDictionary *info = [NSMutableDictionary dictionary];
[info setObject:renderedData forKey:@"output"];
[info setObject:[NSValue valueWithRect:rect] forKey:@"region"];
doneRendering = FALSE;
[NSThread
detachNewThreadSelector:@selector(_renderViewAsPDF:) toTarget:self
withObject:info];
while (!doneRendering)
{
[NSThread sleepUntilDate:[NSDate
dateWithTimeIntervalSinceNow:0.01]];
}
if ([renderedData length] > 0) {return [renderedData autorelease];}
else
{
[renderedData release];
return nil;
}
}
else
{
return [super dataWithPDFInsideRect:rect];
}
}
- (void)_renderViewAsPDF:(NSDictionary *)info
/* this method is not intended to be called directly */
/* used by dataWithPDFInsideRect: when multithreading is required */
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
BOOL success = FALSE;
NSRect rect = [(NSValue *)[info objectForKey:@"region"] rectValue];
NSMutableData *output = [info objectForKey:@"output"];
success = [[NSPrintOperation PDFOperationWithView:self
insideRect:rect to
Data:output] runOperation];
if (!success) {[output setLength:0];}
doneRendering = TRUE;
[pool release];
}
@end
Hope that gets everything working for you.
Louis
Now, during printing, an alert dialog pops up, telling me that the
print operation failed. Debugging shows that the
[recordView dataWithPDFInsideRect:[recordView bounds]]
message fails; I get the following output in the console window:
*** malloc[4379]: Deallocation of a pointer not malloced:
0xbfffcad0; This could be a double free(), or free() called with the
middle of an allocated block; Try setting environment variable
MallocHelp to see tools to help debug
2003-11-26 00:05:16.166 PayMaker[4379] PMSessionEndDocumentNoDialog
failed (error code = -30879)
2003-11-26 00:05:16.166 PayMaker[4379] *** -[NSAutoreleasePool
dealloc]: Exception ignored while releasing an object in an
autorelease pool: NSInternalInconsistencyException Failed to end
PMPrintContext
I'm stuck at this point. Why does dataWithPDFInsideRect fail within
drawRect? I've tried to put pdfData = [recordView
dataWithPDFInsideRect:[recordView bounds]] into printView's init
method; there it works perfectly, I can later use the pdfData in
drawRect as shown above and the recordView appears on my page as
expected. recordView is not a subview of printView (it does not
matter if it is or not; the error message remains the same).
Thanks for your help!
Beat
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.