diff options
Diffstat (limited to 'Source/WebKit2/UIProcess/API/mac/PDFViewController.mm')
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/PDFViewController.mm | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm b/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm new file mode 100644 index 0000000..a92c2d4 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "PDFViewController.h" + +#import "DataReference.h" +#import "WKAPICast.h" +#import "WKView.h" +#import "WebPageGroup.h" +#import "WebPageProxy.h" +#import "WebPreferences.h" +#import <PDFKit/PDFKit.h> +#import <wtf/text/WTFString.h> + +// Redeclarations of PDFKit notifications. We can't use the API since we use a weak link to the framework. +#define _webkit_PDFViewDisplayModeChangedNotification @"PDFViewDisplayModeChanged" +#define _webkit_PDFViewScaleChangedNotification @"PDFViewScaleChanged" +#define _webkit_PDFViewPageChangedNotification @"PDFViewChangedPage" + +using namespace WebKit; + +@class PDFDocument; +@class PDFView; + +@interface PDFDocument (PDFDocumentDetails) +- (NSPrintOperation *)getPrintOperationForPrintInfo:(NSPrintInfo *)printInfo autoRotate:(BOOL)doRotate; +@end + +extern "C" NSString *_NSPathForSystemFramework(NSString *framework); + +@interface WKPDFView : NSView +{ + PDFViewController* _pdfViewController; + + RetainPtr<NSView> _pdfPreviewView; + PDFView *_pdfView; + BOOL _ignoreScaleAndDisplayModeAndPageNotifications; + BOOL _willUpdatePreferencesSoon; +} + +- (id)initWithFrame:(NSRect)frame PDFViewController:(PDFViewController*)pdfViewController; +- (void)invalidate; +- (PDFView *)pdfView; +- (void)setDocument:(PDFDocument *)pdfDocument; + +- (void)_applyPDFPreferences; + +@end + +@implementation WKPDFView + +- (id)initWithFrame:(NSRect)frame PDFViewController:(PDFViewController*)pdfViewController +{ + if ((self = [super initWithFrame:frame])) { + _pdfViewController = pdfViewController; + + [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + Class previewViewClass = PDFViewController::pdfPreviewViewClass(); + ASSERT(previewViewClass); + + _pdfPreviewView.adoptNS([[previewViewClass alloc] initWithFrame:frame]); + [_pdfPreviewView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [self addSubview:_pdfPreviewView.get()]; + + _pdfView = [_pdfPreviewView.get() performSelector:@selector(pdfView)]; + } + + return self; +} + +- (void)invalidate +{ + _pdfViewController = 0; +} + +- (PDFView *)pdfView +{ + return _pdfView; +} + +- (void)setDocument:(PDFDocument *)pdfDocument +{ + _ignoreScaleAndDisplayModeAndPageNotifications = YES; + [_pdfView setDocument:pdfDocument]; + [self _applyPDFPreferences]; + _ignoreScaleAndDisplayModeAndPageNotifications = NO; +} + +- (void)_applyPDFPreferences +{ + if (!_pdfViewController) + return; + + WebPreferences *preferences = toImpl([_pdfViewController->wkView() pageRef])->pageGroup()->preferences(); + + CGFloat scaleFactor = preferences->pdfScaleFactor(); + if (!scaleFactor) + [_pdfView setAutoScales:YES]; + else { + [_pdfView setAutoScales:NO]; + [_pdfView setScaleFactor:scaleFactor]; + } + [_pdfView setDisplayMode:preferences->pdfDisplayMode()]; +} + +- (void)_updatePreferences:(id)ignored +{ + _willUpdatePreferencesSoon = NO; + + if (!_pdfViewController) + return; + + WebPreferences* preferences = toImpl([_pdfViewController->wkView() pageRef])->pageGroup()->preferences(); + + CGFloat scaleFactor = [_pdfView autoScales] ? 0 : [_pdfView scaleFactor]; + preferences->setPDFScaleFactor(scaleFactor); + preferences->setPDFDisplayMode([_pdfView displayMode]); +} + +- (void)_updatePreferencesSoon +{ + if (_willUpdatePreferencesSoon) + return; + + [self performSelector:@selector(_updatePreferences:) withObject:nil afterDelay:0]; + _willUpdatePreferencesSoon = YES; +} + +- (void)_scaleOrDisplayModeOrPageChanged:(NSNotification *)notification +{ + ASSERT_ARG(notification, [notification object] == _pdfView); + if (!_ignoreScaleAndDisplayModeAndPageNotifications) + [self _updatePreferencesSoon]; +} + +- (void)viewDidMoveToWindow +{ + if (![self window]) + return; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewScaleChangedNotification object:_pdfView]; + [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewDisplayModeChangedNotification object:_pdfView]; + [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewPageChangedNotification object:_pdfView]; +} + +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ + if (![self window]) + return; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter removeObserver:self name:_webkit_PDFViewScaleChangedNotification object:_pdfView]; + [notificationCenter removeObserver:self name:_webkit_PDFViewDisplayModeChangedNotification object:_pdfView]; + [notificationCenter removeObserver:self name:_webkit_PDFViewPageChangedNotification object:_pdfView]; +} + +@end + +namespace WebKit { + +PassOwnPtr<PDFViewController> PDFViewController::create(WKView *wkView) +{ + return adoptPtr(new PDFViewController(wkView)); +} + +PDFViewController::PDFViewController(WKView *wkView) + : m_wkView(wkView) + , m_wkPDFView(AdoptNS, [[WKPDFView alloc] initWithFrame:[m_wkView bounds] PDFViewController:this]) + , m_pdfView([m_wkPDFView.get() pdfView]) +{ + [m_wkView addSubview:m_wkPDFView.get()]; +} + +PDFViewController::~PDFViewController() +{ + [m_wkPDFView.get() removeFromSuperview]; + [m_wkPDFView.get() invalidate]; + m_wkPDFView = nullptr; +} + +static RetainPtr<CFDataRef> convertPostScriptDataSourceToPDF(const CoreIPC::DataReference& dataReference) +{ + // Convert PostScript to PDF using Quartz 2D API + // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html + + CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 }; + RetainPtr<CGPSConverterRef> converter(AdoptCF, CGPSConverterCreate(0, &callbacks, 0)); + ASSERT(converter); + + RetainPtr<NSData> nsData(AdoptNS, [[NSData alloc] initWithBytesNoCopy:const_cast<uint8_t*>(dataReference.data()) length:dataReference.size() freeWhenDone:NO]); + + RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithCFData((CFDataRef)nsData.get())); + ASSERT(provider); + + RetainPtr<CFMutableDataRef> result(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0)); + ASSERT(result); + + RetainPtr<CGDataConsumerRef> consumer(AdoptCF, CGDataConsumerCreateWithCFData(result.get())); + ASSERT(consumer); + + CGPSConverterConvert(converter.get(), provider.get(), consumer.get(), 0); + + if (!result) + return 0; + + return result; +} + +void PDFViewController::setPDFDocumentData(const String& mimeType, const CoreIPC::DataReference& dataReference) +{ + RetainPtr<CFDataRef> data; + + if (equalIgnoringCase(mimeType, "application/postscript")) { + data = convertPostScriptDataSourceToPDF(dataReference); + if (!data) + return; + } else { + // Make sure to copy the data. + data.adoptCF(CFDataCreate(0, dataReference.data(), dataReference.size())); + } + + RetainPtr<PDFDocument> pdfDocument(AdoptNS, [[pdfDocumentClass() alloc] initWithData:(NSData *)data.get()]); + [m_wkPDFView.get() setDocument:pdfDocument.get()]; +} + +double PDFViewController::zoomFactor() const +{ + return [m_pdfView scaleFactor]; +} + +void PDFViewController::setZoomFactor(double zoomFactor) +{ + [m_pdfView setScaleFactor:zoomFactor]; +} + +Class PDFViewController::pdfDocumentClass() +{ + static Class pdfDocumentClass = [pdfKitBundle() classNamed:@"PDFDocument"]; + + return pdfDocumentClass; +} + +Class PDFViewController::pdfPreviewViewClass() +{ + static Class pdfPreviewViewClass = [pdfKitBundle() classNamed:@"PDFPreviewView"]; + + return pdfPreviewViewClass; +} + +NSBundle* PDFViewController::pdfKitBundle() +{ + static NSBundle *pdfKitBundle; + if (pdfKitBundle) + return pdfKitBundle; + + NSString *pdfKitPath = [_NSPathForSystemFramework(@"Quartz.framework") stringByAppendingString:@"/Frameworks/PDFKit.framework"]; + if (!pdfKitPath) { + LOG_ERROR("Couldn't find PDFKit.framework"); + return nil; + } + + pdfKitBundle = [NSBundle bundleWithPath:pdfKitPath]; + if (![pdfKitBundle load]) + LOG_ERROR("Couldn't load PDFKit.framework"); + return pdfKitBundle; +} + +NSPrintOperation *PDFViewController::makePrintOperation(NSPrintInfo *printInfo) +{ + return [[m_pdfView document] getPrintOperationForPrintInfo:printInfo autoRotate:YES]; +} + +} // namespace WebKit |