2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #import "WKPrintingView.h"
32 #import "WebPageProxy.h"
34 using namespace WebKit;
35 using namespace WebCore;
37 NSString * const WebKitOriginalTopPrintingMarginKey = @"WebKitOriginalTopMargin";
38 NSString * const WebKitOriginalBottomPrintingMarginKey = @"WebKitOriginalBottomMargin";
40 NSString * const NSPrintInfoDidChangeNotification = @"NSPrintInfoDidChange";
42 @implementation WKPrintingView
44 - (id)initWithFrameProxy:(WebFrameProxy*)frame
46 self = [super init]; // No frame rect to pass to NSView.
60 - (void)_suspendAutodisplay
62 // A drawRect: call on WKView causes a switch to screen mode, which is slow due to relayout, and we want to avoid that.
63 // Disabling autodisplay will prevent random updates from causing this, but resizing the window will still work.
64 if (_autodisplayResumeTimer) {
65 [_autodisplayResumeTimer invalidate];
66 _autodisplayResumeTimer = nil;
68 _webFrame->page()->setAutodisplay(false);
71 - (void)_delayedResumeAutodisplayTimerFired
73 ASSERT(isMainThread());
75 _autodisplayResumeTimer = nil;
76 _webFrame->page()->setAutodisplay(true);
79 - (void)_delayedResumeAutodisplay
81 // AppKit calls endDocument/beginDocument when print option change. We don't want to switch between print and screen mode just for that,
82 // and enabling autodisplay may result in switching into screen mode. So, autodisplay is only resumed on next run loop iteration.
83 if (!_autodisplayResumeTimer) {
84 _autodisplayResumeTimer = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(_delayedResumeAutodisplayTimerFired) userInfo:nil repeats:NO];
85 // The timer must be scheduled on main thread, because printing thread may finish before it fires.
86 [[NSRunLoop mainRunLoop] addTimer:_autodisplayResumeTimer forMode:NSDefaultRunLoopMode];
90 - (void)_adjustPrintingMarginsForHeaderAndFooter
92 NSPrintInfo *info = [_printOperation printInfo];
93 NSMutableDictionary *infoDictionary = [info dictionary];
95 // We need to modify the top and bottom margins in the NSPrintInfo to account for the space needed by the
96 // header and footer. Because this method can be called more than once on the same NSPrintInfo (see 5038087),
97 // we stash away the unmodified top and bottom margins the first time this method is called, and we read from
98 // those stashed-away values on subsequent calls.
99 double originalTopMargin;
100 double originalBottomMargin;
101 NSNumber *originalTopMarginNumber = [infoDictionary objectForKey:WebKitOriginalTopPrintingMarginKey];
102 if (!originalTopMarginNumber) {
103 ASSERT(![infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey]);
104 originalTopMargin = [info topMargin];
105 originalBottomMargin = [info bottomMargin];
106 [infoDictionary setObject:[NSNumber numberWithDouble:originalTopMargin] forKey:WebKitOriginalTopPrintingMarginKey];
107 [infoDictionary setObject:[NSNumber numberWithDouble:originalBottomMargin] forKey:WebKitOriginalBottomPrintingMarginKey];
109 ASSERT([originalTopMarginNumber isKindOfClass:[NSNumber class]]);
110 ASSERT([[infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey] isKindOfClass:[NSNumber class]]);
111 originalTopMargin = [originalTopMarginNumber doubleValue];
112 originalBottomMargin = [[infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey] doubleValue];
115 CGFloat scale = [info scalingFactor];
116 [info setTopMargin:originalTopMargin + _webFrame->page()->headerHeight(_webFrame.get()) * scale];
117 [info setBottomMargin:originalBottomMargin + _webFrame->page()->footerHeight(_webFrame.get()) * scale];
120 - (BOOL)_isPrintingPreview
122 // <rdar://problem/8901041> Please add an API returning whether the current print operation is for preview.
123 // Assuming that if NSPrintOperation is allowed to spawn a thread for printing, it will. Print preview doesn't spawn a thread.
124 return !_isPrintingFromSecondaryThread;
127 - (void)_updatePreview
129 // <rdar://problem/8900923> Please add an API to force print preview update.
130 _isForcingPreviewUpdate = YES;
131 [[NSNotificationCenter defaultCenter] postNotificationName:NSPrintInfoDidChangeNotification object:nil];
132 _isForcingPreviewUpdate = NO;
135 - (BOOL)_hasPageRects
137 // WebCore always prints at least one page.
138 return !_printingPageRects.isEmpty();
141 - (NSUInteger)_firstPrintedPageNumber
143 // Need to directly access the dictionary because -[NSPrintOperation pageRange] verifies pagination, potentially causing recursion.
144 return [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintFirstPage] unsignedIntegerValue];
147 - (NSUInteger)_lastPrintedPageNumber
149 ASSERT([self _hasPageRects]);
151 // Need to directly access the dictionary because -[NSPrintOperation pageRange] verifies pagination, potentially causing recursion.
152 NSUInteger firstPage = [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintFirstPage] unsignedIntegerValue];
153 NSUInteger lastPage = [[[[_printOperation printInfo] dictionary] objectForKey:NSPrintLastPage] unsignedIntegerValue];
154 if (lastPage - firstPage >= _printingPageRects.size())
155 return _printingPageRects.size();
159 - (uint64_t)_expectedPreviewCallbackForRect:(const IntRect&)rect
161 for (HashMap<uint64_t, WebCore::IntRect>::iterator iter = _expectedPreviewCallbacks.begin(); iter != _expectedPreviewCallbacks.end(); ++iter) {
162 if (iter->second == rect)
168 struct IPCCallbackContext {
169 RetainPtr<WKPrintingView> view;
173 static void pageDidDrawToPDF(WKDataRef dataRef, WKErrorRef, void* untypedContext)
175 ASSERT(isMainThread());
177 OwnPtr<IPCCallbackContext> context = adoptPtr(static_cast<IPCCallbackContext*>(untypedContext));
178 WKPrintingView *view = context->view.get();
179 WebData* data = toImpl(dataRef);
181 if (context->callbackID == view->_expectedPrintCallback) {
182 ASSERT(![view _isPrintingPreview]);
183 ASSERT(view->_printedPagesData.isEmpty());
184 ASSERT(!view->_printedPagesPDFDocument);
186 view->_printedPagesData.append(data->bytes(), data->size());
187 view->_expectedPrintCallback = 0;
188 view->_printingCallbackCondition.signal();
190 // If the user has already changed print setup, then this response is obsolete. And this callback is not in response to the latest request,
191 // then the user has already moved to another page - we'll cache the response, but won't draw it.
192 HashMap<uint64_t, WebCore::IntRect>::iterator iter = view->_expectedPreviewCallbacks.find(context->callbackID);
193 if (iter != view->_expectedPreviewCallbacks.end()) {
194 ASSERT([view _isPrintingPreview]);
197 pair<HashMap<WebCore::IntRect, Vector<uint8_t> >::iterator, bool> entry = view->_pagePreviews.add(iter->second, Vector<uint8_t>());
198 entry.first->second.append(data->bytes(), data->size());
200 bool receivedResponseToLatestRequest = view->_latestExpectedPreviewCallback == context->callbackID;
201 view->_latestExpectedPreviewCallback = 0;
202 view->_expectedPreviewCallbacks.remove(context->callbackID);
203 if (receivedResponseToLatestRequest)
204 [view _updatePreview];
209 - (void)_preparePDFDataForPrintingOnSecondaryThread
211 ASSERT(isMainThread());
213 if (!_webFrame->page()) {
214 _printingCallbackCondition.signal();
218 MutexLocker lock(_printingCallbackMutex);
220 ASSERT([self _hasPageRects]);
221 ASSERT(_printedPagesData.isEmpty());
222 ASSERT(!_printedPagesPDFDocument);
223 ASSERT(!_expectedPrintCallback);
225 NSUInteger firstPage = [self _firstPrintedPageNumber];
226 NSUInteger lastPage = [self _lastPrintedPageNumber];
228 ASSERT(firstPage > 0);
229 ASSERT(firstPage <= lastPage);
230 LOG(View, "WKPrintingView requesting PDF data for pages %u...%u", firstPage, lastPage);
232 // Return to printing mode if we're already back to screen (e.g. due to window resizing).
233 _webFrame->page()->beginPrinting(_webFrame.get(), PrintInfo([_printOperation printInfo]));
235 IPCCallbackContext* context = new IPCCallbackContext;
236 RefPtr<DataCallback> callback = DataCallback::create(context, pageDidDrawToPDF);
237 _expectedPrintCallback = callback->callbackID();
239 context->view = self;
240 context->callbackID = callback->callbackID();
242 _webFrame->page()->drawPagesToPDF(_webFrame.get(), firstPage - 1, lastPage - firstPage + 1, callback.get());
245 static void pageDidComputePageRects(const Vector<WebCore::IntRect>& pageRects, double totalScaleFactorForPrinting, WKErrorRef, void* untypedContext)
247 ASSERT(isMainThread());
249 OwnPtr<IPCCallbackContext> context = adoptPtr(static_cast<IPCCallbackContext*>(untypedContext));
250 WKPrintingView *view = context->view.get();
252 // If the user has already changed print setup, then this response is obsolete.
253 if (context->callbackID == view->_expectedComputedPagesCallback) {
254 ASSERT(isMainThread());
255 ASSERT(view->_expectedPreviewCallbacks.isEmpty());
256 ASSERT(!view->_latestExpectedPreviewCallback);
257 ASSERT(!view->_expectedPrintCallback);
258 ASSERT(view->_pagePreviews.isEmpty());
259 view->_expectedComputedPagesCallback = 0;
261 view->_printingPageRects = pageRects;
262 view->_totalScaleFactorForPrinting = totalScaleFactorForPrinting;
264 const IntRect& lastPrintingPageRect = view->_printingPageRects[view->_printingPageRects.size() - 1];
265 NSRect newFrameSize = NSMakeRect(0, 0,
266 ceil(lastPrintingPageRect.maxX() * view->_totalScaleFactorForPrinting),
267 ceil(lastPrintingPageRect.maxY() * view->_totalScaleFactorForPrinting));
268 LOG(View, "WKPrintingView setting frame size to x:%g y:%g width:%g height:%g", newFrameSize.origin.x, newFrameSize.origin.y, newFrameSize.size.width, newFrameSize.size.height);
269 [view setFrame:newFrameSize];
271 if ([view _isPrintingPreview]) {
272 // Show page count, and ask for an actual image to replace placeholder.
273 [view _updatePreview];
275 // When printing, request everything we'll need beforehand.
276 [view _preparePDFDataForPrintingOnSecondaryThread];
281 - (BOOL)_askPageToComputePageRects
283 ASSERT(isMainThread());
285 if (!_webFrame->page())
288 ASSERT(!_expectedComputedPagesCallback);
290 IPCCallbackContext* context = new IPCCallbackContext;
291 RefPtr<ComputedPagesCallback> callback = ComputedPagesCallback::create(context, pageDidComputePageRects);
292 _expectedComputedPagesCallback = callback->callbackID();
293 context->view = self;
294 context->callbackID = _expectedComputedPagesCallback;
296 _webFrame->page()->computePagesForPrinting(_webFrame.get(), PrintInfo([_printOperation printInfo]), callback.release());
300 static void prepareDataForPrintingOnSecondaryThread(void* untypedContext)
302 ASSERT(isMainThread());
304 WKPrintingView *view = static_cast<WKPrintingView *>(untypedContext);
305 MutexLocker lock(view->_printingCallbackMutex);
307 // We may have received page rects while a message to call this function traveled from secondary thread to main one.
308 if ([view _hasPageRects]) {
309 [view _preparePDFDataForPrintingOnSecondaryThread];
313 // A request for pages has already been made, just wait for it to finish.
314 if (view->_expectedComputedPagesCallback)
317 [view _askPageToComputePageRects];
320 - (BOOL)knowsPageRange:(NSRangePointer)range
322 LOG(View, "-[WKPrintingView knowsPageRange:], %s, %s", [self _hasPageRects] ? "print data is available" : "print data is not available yet", isMainThread() ? "on main thread" : "on secondary thread");
323 ASSERT(_printOperation == [NSPrintOperation currentOperation]);
325 // Assuming that once we switch to printing from a secondary thread, we don't go back.
326 ASSERT(!_isPrintingFromSecondaryThread || !isMainThread());
328 _isPrintingFromSecondaryThread = YES;
330 [self _suspendAutodisplay];
332 [self _adjustPrintingMarginsForHeaderAndFooter];
334 if ([self _hasPageRects])
335 *range = NSMakeRange(1, _printingPageRects.size());
336 else if (!isMainThread()) {
337 ASSERT(![self _isPrintingPreview]);
338 MutexLocker lock(_printingCallbackMutex);
339 callOnMainThread(prepareDataForPrintingOnSecondaryThread, self);
340 _printingCallbackCondition.wait(_printingCallbackMutex);
341 *range = NSMakeRange(1, _printingPageRects.size());
343 ASSERT([self _isPrintingPreview]);
345 [self _askPageToComputePageRects];
347 *range = NSMakeRange(1, NSIntegerMax);
352 - (unsigned)_pageForRect:(NSRect)rect
354 // Assuming that rect exactly matches one of the pages.
355 for (size_t i = 0; i < _printingPageRects.size(); ++i) {
356 IntRect currentRect(_printingPageRects[i]);
357 currentRect.scale(_totalScaleFactorForPrinting);
358 if (rect.origin.y == currentRect.y() && rect.origin.x == currentRect.x())
361 ASSERT_NOT_REACHED();
362 return 0; // Invalid page number.
365 - (void)_drawPDFDocument:(CGPDFDocumentRef)pdfDocument page:(unsigned)page atPoint:(NSPoint)point
368 LOG_ERROR("Couldn't create a PDF document with data passed for preview");
372 CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdfDocument, page);
374 LOG_ERROR("Preview data doesn't have page %d", page);
378 NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
379 CGContextRef context = static_cast<CGContextRef>([nsGraphicsContext graphicsPort]);
381 CGContextSaveGState(context);
382 CGContextTranslateCTM(context, point.x, point.y);
383 CGContextScaleCTM(context, _totalScaleFactorForPrinting, -_totalScaleFactorForPrinting);
384 CGContextTranslateCTM(context, 0, -CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox).size.height);
385 CGContextDrawPDFPage(context, pdfPage);
386 CGContextRestoreGState(context);
389 - (void)_drawPreview:(NSRect)nsRect
391 ASSERT(isMainThread());
393 IntRect rect(nsRect);
394 rect.scale(1 / _totalScaleFactorForPrinting);
395 HashMap<WebCore::IntRect, Vector<uint8_t> >::iterator pagePreviewIterator = _pagePreviews.find(rect);
396 if (pagePreviewIterator == _pagePreviews.end()) {
397 // It's too early to ask for page preview if we don't even know page size and scale.
398 if ([self _hasPageRects]) {
399 if (uint64_t existingCallback = [self _expectedPreviewCallbackForRect:rect]) {
400 // We've already asked for a preview of this page, and are waiting for response.
401 // There is no need to ask again.
402 _latestExpectedPreviewCallback = existingCallback;
404 // Preview isn't available yet, request it asynchronously.
405 if (!_webFrame->page())
408 // Return to printing mode if we're already back to screen (e.g. due to window resizing).
409 _webFrame->page()->beginPrinting(_webFrame.get(), PrintInfo([_printOperation printInfo]));
411 IPCCallbackContext* context = new IPCCallbackContext;
412 RefPtr<DataCallback> callback = DataCallback::create(context, pageDidDrawToPDF);
413 _latestExpectedPreviewCallback = callback->callbackID();
414 _expectedPreviewCallbacks.add(_latestExpectedPreviewCallback, rect);
416 context->view = self;
417 context->callbackID = callback->callbackID();
419 _webFrame->page()->drawRectToPDF(_webFrame.get(), rect, callback.get());
424 // FIXME: Draw a placeholder
428 const Vector<uint8_t>& pdfData = pagePreviewIterator->second;
429 RetainPtr<CGDataProviderRef> pdfDataProvider(AdoptCF, CGDataProviderCreateWithData(0, pdfData.data(), pdfData.size(), 0));
430 RetainPtr<CGPDFDocumentRef> pdfDocument(AdoptCF, CGPDFDocumentCreateWithProvider(pdfDataProvider.get()));
432 [self _drawPDFDocument:pdfDocument.get() page:1 atPoint:NSMakePoint(nsRect.origin.x, nsRect.origin.y)];
435 - (void)drawRect:(NSRect)nsRect
437 LOG(View, "WKPrintingView printing rect x:%g, y:%g, width:%g, height:%g%s", nsRect.origin.x, nsRect.origin.y, nsRect.size.width, nsRect.size.height, [self _isPrintingPreview] ? " for preview" : "");
439 ASSERT(_printOperation == [NSPrintOperation currentOperation]);
441 if (!_webFrame->page())
444 if ([self _isPrintingPreview]) {
445 [self _drawPreview:nsRect];
449 ASSERT(!isMainThread());
450 ASSERT(!_printedPagesData.isEmpty()); // Prepared by knowsPageRange:
452 if (!_printedPagesPDFDocument) {
453 RetainPtr<CGDataProviderRef> pdfDataProvider(AdoptCF, CGDataProviderCreateWithData(0, _printedPagesData.data(), _printedPagesData.size(), 0));
454 _printedPagesPDFDocument.adoptCF(CGPDFDocumentCreateWithProvider(pdfDataProvider.get()));
457 unsigned printedPageNumber = [self _pageForRect:nsRect] - [self _firstPrintedPageNumber] + 1;
458 [self _drawPDFDocument:_printedPagesPDFDocument.get() page:printedPageNumber atPoint:NSMakePoint(nsRect.origin.x, nsRect.origin.y)];
461 - (void)_drawPageBorderWithSizeOnMainThread:(NSSize)borderSize
463 ASSERT(isMainThread());
465 // When printing from a secondary thread, the main thread doesn't have graphics context and printing operation set up.
466 NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
467 [NSGraphicsContext setCurrentContext:[_printOperation context]];
469 ASSERT(![NSPrintOperation currentOperation]);
470 [NSPrintOperation setCurrentOperation:_printOperation];
472 [self drawPageBorderWithSize:borderSize];
474 [NSPrintOperation setCurrentOperation:nil];
475 [NSGraphicsContext setCurrentContext:currentContext];
478 - (void)drawPageBorderWithSize:(NSSize)borderSize
480 ASSERT(NSEqualSizes(borderSize, [[_printOperation printInfo] paperSize]));
481 ASSERT(_printOperation == [NSPrintOperation currentOperation]);
483 if (!isMainThread()) {
484 // Don't call the client from a secondary thread.
485 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[WKPrintingView instanceMethodSignatureForSelector:@selector(_drawPageBorderWithSizeOnMainThread:)]];
486 [invocation setSelector:@selector(_drawPageBorderWithSizeOnMainThread:)];
487 [invocation setArgument:&borderSize atIndex:2];
488 [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:self waitUntilDone:YES];
492 if (!_webFrame->page())
495 // The header and footer rect height scales with the page, but the width is always
496 // all the way across the printed page (inset by printing margins).
497 NSPrintInfo *printInfo = [_printOperation printInfo];
498 CGFloat scale = [printInfo scalingFactor];
499 NSSize paperSize = [printInfo paperSize];
500 CGFloat headerFooterLeft = [printInfo leftMargin] / scale;
501 CGFloat headerFooterWidth = (paperSize.width - ([printInfo leftMargin] + [printInfo rightMargin])) / scale;
502 NSRect footerRect = NSMakeRect(headerFooterLeft, [printInfo bottomMargin] / scale - _webFrame->page()->footerHeight(_webFrame.get()), headerFooterWidth, _webFrame->page()->footerHeight(_webFrame.get()));
503 NSRect headerRect = NSMakeRect(headerFooterLeft, (paperSize.height - [printInfo topMargin]) / scale, headerFooterWidth, _webFrame->page()->headerHeight(_webFrame.get()));
505 NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
506 [currentContext saveGraphicsState];
507 NSRectClip(headerRect);
508 _webFrame->page()->drawHeader(_webFrame.get(), headerRect);
509 [currentContext restoreGraphicsState];
511 [currentContext saveGraphicsState];
512 NSRectClip(footerRect);
513 _webFrame->page()->drawFooter(_webFrame.get(), footerRect);
514 [currentContext restoreGraphicsState];
517 - (NSRect)rectForPage:(NSInteger)page
519 ASSERT(_printOperation == [NSPrintOperation currentOperation]);
520 if (![self _hasPageRects]) {
521 LOG(View, "-[WKPrintingView rectForPage:%d] - data is not yet available", (int)page);
522 // We must be still calculating the page range.
523 ASSERT(_expectedComputedPagesCallback);
524 return NSMakeRect(0, 0, 1, 1);
527 IntRect rect = _printingPageRects[page - 1];
528 rect.scale(_totalScaleFactorForPrinting);
529 LOG(View, "-[WKPrintingView rectForPage:%d] -> x %d, y %d, width %d, height %d", (int)page, rect.x(), rect.y(), rect.width(), rect.height());
533 // Temporary workaround for <rdar://problem/8944535>. Force correct printout positioning.
534 - (NSPoint)locationOfPrintRect:(NSRect)aRect
536 ASSERT(_printOperation == [NSPrintOperation currentOperation]);
537 return NSMakePoint([[_printOperation printInfo] leftMargin], [[_printOperation printInfo] bottomMargin]);
540 - (void)beginDocument
542 ASSERT(_printOperation == [NSPrintOperation currentOperation]);
544 // Forcing preview update gets us here, but page setup hasn't actually changed.
545 if (_isForcingPreviewUpdate)
548 LOG(View, "-[WKPrintingView beginDocument]");
550 [super beginDocument];
552 [self _suspendAutodisplay];
557 ASSERT(_printOperation == [NSPrintOperation currentOperation]);
559 // Forcing preview update gets us here, but page setup hasn't actually changed.
560 if (_isForcingPreviewUpdate)
563 LOG(View, "-[WKPrintingView endDocument] - clearing cached data");
565 // Both existing data and pending responses are now obsolete.
566 _printingPageRects.clear();
567 _totalScaleFactorForPrinting = 1;
568 _pagePreviews.clear();
569 _printedPagesData.clear();
570 _printedPagesPDFDocument = nullptr;
571 _expectedComputedPagesCallback = 0;
572 _expectedPreviewCallbacks.clear();
573 _latestExpectedPreviewCallback = 0;
574 _expectedPrintCallback = 0;
576 [self _delayedResumeAutodisplay];