2 * Copyright (C) 2014 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.
31 #import "WKPDFPageNumberIndicator.h"
32 #import "WKWebViewInternal.h"
33 #import <CorePDF/UIPDFDocument.h>
34 #import <CorePDF/UIPDFPage.h>
35 #import <CorePDF/UIPDFPageView.h>
36 #import <UIKit/UIScrollView_Private.h>
37 #import <WebCore/FloatRect.h>
38 #import <wtf/RetainPtr.h>
39 #import <wtf/Vector.h>
41 using namespace WebCore;
43 const CGFloat pdfPageMargin = 8;
44 const CGFloat pdfMinimumZoomScale = 1;
45 const CGFloat pdfMaximumZoomScale = 5;
47 const float overdrawHeightMultiplier = 1.5;
49 static const CGFloat smartMagnificationElementPadding = 0.05;
53 RetainPtr<UIPDFPageView> view;
54 RetainPtr<UIPDFPage> page;
57 @implementation WKPDFView {
58 RetainPtr<UIPDFDocument> _pdfDocument;
59 RetainPtr<NSString> _suggestedFilename;
60 RetainPtr<WKPDFPageNumberIndicator> _pageNumberIndicator;
62 Vector<PDFPageInfo> _pages;
63 unsigned _centerPageNumber;
66 CGSize _overlaidAccessoryViewsInset;
68 UIScrollView *_scrollView;
69 UIView *_fixedOverlayView;
74 - (instancetype)web_initWithFrame:(CGRect)frame webView:(WKWebView *)webView
76 if (!(self = [super initWithFrame:frame]))
79 self.backgroundColor = [UIColor grayColor];
83 _scrollView = webView.scrollView;
84 [_scrollView setMinimumZoomScale:pdfMinimumZoomScale];
85 [_scrollView setMaximumZoomScale:pdfMaximumZoomScale];
86 [_scrollView setBackgroundColor:[UIColor grayColor]];
94 [_pageNumberIndicator removeFromSuperview];
98 - (NSString *)suggestedFilename
100 return _suggestedFilename.get();
103 - (CGPDFDocumentRef)pdfDocument
105 return [_pdfDocument CGDocument];
110 for (auto& page : _pages) {
111 [page.view removeFromSuperview];
112 [page.view setDelegate:nil];
118 - (void)web_setContentProviderData:(NSData *)data suggestedFilename:(NSString *)filename
120 _suggestedFilename = adoptNS([filename copy]);
124 RetainPtr<CGDataProvider> dataProvider = adoptCF(CGDataProviderCreateWithCFData((CFDataRef)data));
125 RetainPtr<CGPDFDocumentRef> cgPDFDocument = adoptCF(CGPDFDocumentCreateWithProvider(dataProvider.get()));
126 _pdfDocument = adoptNS([[UIPDFDocument alloc] initWithCGPDFDocument:cgPDFDocument.get()]);
128 [self _computePageAndDocumentFrames];
129 [self _revalidateViews];
132 - (void)web_setMinimumSize:(CGSize)size
136 [self _computePageAndDocumentFrames];
137 [self _revalidateViews];
140 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
142 if (scrollView.isZoomBouncing || scrollView._isAnimatingZoom)
145 [self _revalidateViews];
146 [_pageNumberIndicator show];
149 - (void)_revalidateViews
154 CGRect targetRect = [_scrollView convertRect:_scrollView.bounds toView:self];
156 // We apply overdraw after applying scale in order to avoid excessive
157 // memory use caused by scaling the overdraw.
158 CGRect targetRectWithOverdraw = CGRectInset(targetRect, 0, -targetRect.size.height * overdrawHeightMultiplier);
159 CGRect targetRectForCenterPage = CGRectInset(targetRect, 0, targetRect.size.height / 2 - pdfPageMargin * 2);
161 _centerPageNumber = 0;
162 unsigned currentPage = 0;
164 for (auto& pageInfo : _pages) {
167 if (!CGRectIntersectsRect(pageInfo.frame, targetRectWithOverdraw)) {
168 [pageInfo.view removeFromSuperview];
169 pageInfo.view = nullptr;
173 if (!_centerPageNumber && CGRectIntersectsRect(pageInfo.frame, targetRectForCenterPage))
174 _centerPageNumber = currentPage;
179 pageInfo.view = adoptNS([[UIPDFPageView alloc] initWithPage:pageInfo.page.get() tiledContent:YES]);
180 [pageInfo.view setUseBackingLayer:YES];
181 [pageInfo.view setDelegate:self];
182 [self addSubview:pageInfo.view.get()];
184 [pageInfo.view setFrame:pageInfo.frame];
185 [pageInfo.view contentLayer].contentsScale = self.window.screen.scale;
188 [self _updatePageNumberIndicator];
191 - (CGPoint)_offsetForPageNumberIndicator
193 UIEdgeInsets contentInset = [_webView _computedContentInset];
194 return CGPointMake(contentInset.left, contentInset.top + _overlaidAccessoryViewsInset.height);
197 - (void)_updatePageNumberIndicator
199 if (!_pageNumberIndicator)
200 _pageNumberIndicator = adoptNS([[WKPDFPageNumberIndicator alloc] initWithFrame:CGRectZero]);
202 [_fixedOverlayView addSubview:_pageNumberIndicator.get()];
204 [_pageNumberIndicator setCurrentPageNumber:_centerPageNumber];
205 [_pageNumberIndicator moveToPoint:[self _offsetForPageNumberIndicator] animated:NO];
208 - (void)web_setOverlaidAccessoryViewsInset:(CGSize)inset
210 _overlaidAccessoryViewsInset = inset;
211 [_pageNumberIndicator moveToPoint:[self _offsetForPageNumberIndicator] animated:YES];
214 - (void)web_computedContentInsetDidChange
216 [self _updatePageNumberIndicator];
219 - (void)web_setFixedOverlayView:(UIView *)fixedOverlayView
221 _fixedOverlayView = fixedOverlayView;
223 if (_pageNumberIndicator)
224 [_fixedOverlayView addSubview:_pageNumberIndicator.get()];
227 - (void)_computePageAndDocumentFrames
229 NSUInteger pageCount = [_pdfDocument numberOfPages];
230 [_pageNumberIndicator setPageCount:pageCount];
234 _pages.reserveCapacity(pageCount);
236 CGRect pageFrame = CGRectMake(0, 0, _minimumSize.width, _minimumSize.height);
237 for (NSUInteger pageNumber = 0; pageNumber < pageCount; ++pageNumber) {
238 UIPDFPage *page = [_pdfDocument pageAtIndex:pageNumber];
242 CGSize pageSize = [page size];
243 pageFrame.size.height = pageSize.height / pageSize.width * pageFrame.size.width;
244 CGRect pageFrameWithMarginApplied = CGRectInset(pageFrame, pdfPageMargin, pdfPageMargin);
246 PDFPageInfo pageInfo;
247 pageInfo.page = page;
248 pageInfo.frame = pageFrameWithMarginApplied;
249 _pages.append(pageInfo);
250 pageFrame.origin.y += pageFrame.size.height - pdfPageMargin;
253 CGFloat scale = _scrollView.zoomScale;
254 CGRect newFrame = [self frame];
255 newFrame.size.width = _minimumSize.width * scale;
256 newFrame.size.height = std::max(pageFrame.origin.y + pdfPageMargin, _minimumSize.height) * scale;
258 [self setFrame:newFrame];
259 [_scrollView setContentSize:newFrame.size];
263 - (void)zoom:(UIPDFPageView *)pageView to:(CGRect)targetRect atPoint:(CGPoint)origin kind:(UIPDFObjectKind)kind
265 _isStartingZoom = YES;
267 BOOL isImage = kind == kUIPDFObjectKindGraphic;
270 targetRect = CGRectInset(targetRect, smartMagnificationElementPadding * targetRect.size.width, smartMagnificationElementPadding * targetRect.size.height);
272 CGRect rectInDocumentCoordinates = [pageView convertRect:targetRect toView:self];
273 CGPoint originInDocumentCoordinates = [pageView convertPoint:origin toView:self];
275 [_webView _zoomToRect:rectInDocumentCoordinates withOrigin:originInDocumentCoordinates fitEntireRect:isImage minimumScale:pdfMinimumZoomScale maximumScale:pdfMaximumZoomScale minimumScrollDistance:0];
277 _isStartingZoom = NO;
280 - (void)resetZoom:(UIPDFPageView *)pageView
282 _isStartingZoom = YES;
284 CGRect scrollViewBounds = _scrollView.bounds;
285 CGPoint centerOfPageInDocumentCoordinates = [_scrollView convertPoint:CGPointMake(CGRectGetMidX(scrollViewBounds), CGRectGetMidY(scrollViewBounds)) toView:self];
286 [_webView _zoomOutWithOrigin:centerOfPageInDocumentCoordinates];
288 _isStartingZoom = NO;
293 #endif /* PLATFORM(IOS) */