blob: 69e12821e57ebf5a436fdebed8457a52ff6f4ef7 [file] [log] [blame]
/*
* Copyright (C) 2014 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 "config.h"
#import "WKPDFView.h"
#if PLATFORM(IOS)
#import "WKPDFPageNumberIndicator.h"
#import <CorePDF/UIPDFDocument.h>
#import <CorePDF/UIPDFPageView.h>
#import <WebCore/FloatRect.h>
#import <wtf/RetainPtr.h>
#import <wtf/Vector.h>
using namespace WebCore;
const CGFloat pdfPageMargin = 8;
const CGFloat pdfMinimumZoomScale = 1;
const CGFloat pdfMaximumZoomScale = 5;
const float overdrawHeightMultiplier = 1.5;
typedef struct {
CGRect frame;
RetainPtr<UIPDFPageView> view;
RetainPtr<UIPDFPage> page;
} PDFPageInfo;
@implementation WKPDFView {
RetainPtr<UIPDFDocument> _pdfDocument;
RetainPtr<NSString> _suggestedFilename;
RetainPtr<WKPDFPageNumberIndicator> _pageNumberIndicator;
Vector<PDFPageInfo> _pages;
CGRect _documentFrame;
unsigned _centerPageNumber;
CGSize _minimumSize;
CGSize _overlaidAccessoryViewsInset;
UIEdgeInsets _obscuredInsets;
UIScrollView *_scrollView;
UIView *_fixedOverlayView;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (!(self = [super initWithFrame:frame]))
return nil;
self.backgroundColor = [UIColor grayColor];
return self;
}
- (void)dealloc
{
[_pageNumberIndicator removeFromSuperview];
[super dealloc];
}
- (NSString *)suggestedFilename
{
return _suggestedFilename.get();
}
- (CGPDFDocumentRef)pdfDocument
{
return [_pdfDocument CGDocument];
}
- (void)web_setContentProviderData:(NSData *)data suggestedFilename:(NSString *)filename
{
_suggestedFilename = adoptNS([filename copy]);
for (auto& page : _pages)
[page.view removeFromSuperview];
_pages.clear();
RetainPtr<CGDataProvider> dataProvider = adoptCF(CGDataProviderCreateWithCFData((CFDataRef)data));
RetainPtr<CGPDFDocumentRef> cgPDFDocument = adoptCF(CGPDFDocumentCreateWithProvider(dataProvider.get()));
_pdfDocument = adoptNS([[UIPDFDocument alloc] initWithCGPDFDocument:cgPDFDocument.get()]);
[self _computePageAndDocumentFrames];
[self _revalidateViews];
}
- (void)web_setMinimumSize:(CGSize)size
{
_minimumSize = size;
[self _computePageAndDocumentFrames];
[self _revalidateViews];
}
- (void)web_setScrollView:(UIScrollView *)scrollView
{
_scrollView = scrollView;
[_scrollView setMinimumZoomScale:pdfMinimumZoomScale];
[_scrollView setMaximumZoomScale:pdfMaximumZoomScale];
[_scrollView setContentSize:_documentFrame.size];
[_scrollView setBackgroundColor:[UIColor grayColor]];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.isZoomBouncing)
return;
[self _revalidateViews];
[_pageNumberIndicator show];
}
- (void)_revalidateViews
{
CGRect targetRect = [_scrollView convertRect:_scrollView.bounds toView:self];
// We apply overdraw after applying scale in order to avoid excessive
// memory use caused by scaling the overdraw.
CGRect targetRectWithOverdraw = CGRectInset(targetRect, 0, -targetRect.size.height * overdrawHeightMultiplier);
CGRect targetRectForCenterPage = CGRectInset(targetRect, 0, targetRect.size.height / 2 - pdfPageMargin * 2);
_centerPageNumber = 0;
unsigned currentPage = 0;
for (auto& pageInfo : _pages) {
++currentPage;
if (!CGRectIntersectsRect(pageInfo.frame, targetRectWithOverdraw)) {
[pageInfo.view removeFromSuperview];
pageInfo.view = nullptr;
continue;
}
if (!_centerPageNumber && CGRectIntersectsRect(pageInfo.frame, targetRectForCenterPage))
_centerPageNumber = currentPage;
if (pageInfo.view)
continue;
pageInfo.view = adoptNS([[UIPDFPageView alloc] initWithPage:pageInfo.page.get() tiledContent:YES]);
[pageInfo.view setUseBackingLayer:YES];
[self addSubview:pageInfo.view.get()];
[pageInfo.view setFrame:pageInfo.frame];
[pageInfo.view contentLayer].contentsScale = self.window.screen.scale;
}
[self _updatePageNumberIndicator];
}
- (CGPoint)_offsetForPageNumberIndicator
{
return CGPointMake(_obscuredInsets.left, _obscuredInsets.top + _overlaidAccessoryViewsInset.height);
}
- (void)_updatePageNumberIndicator
{
if (!_pageNumberIndicator)
_pageNumberIndicator = adoptNS([[WKPDFPageNumberIndicator alloc] initWithFrame:CGRectZero]);
[_fixedOverlayView addSubview:_pageNumberIndicator.get()];
[_pageNumberIndicator setCurrentPageNumber:_centerPageNumber];
[_pageNumberIndicator moveToPoint:[self _offsetForPageNumberIndicator] animated:NO];
}
- (void)web_setObscuredInsets:(UIEdgeInsets)insets
{
if (UIEdgeInsetsEqualToEdgeInsets(insets, _obscuredInsets))
return;
_obscuredInsets = insets;
[self _updatePageNumberIndicator];
}
- (void)web_setOverlaidAccessoryViewsInset:(CGSize)inset
{
_overlaidAccessoryViewsInset = inset;
[_pageNumberIndicator moveToPoint:[self _offsetForPageNumberIndicator] animated:YES];
}
- (void)web_setFixedOverlayView:(UIView *)fixedOverlayView
{
_fixedOverlayView = fixedOverlayView;
if (_pageNumberIndicator)
[_fixedOverlayView addSubview:_pageNumberIndicator.get()];
}
- (void)_computePageAndDocumentFrames
{
NSUInteger pageCount = [_pdfDocument numberOfPages];
[_pageNumberIndicator setPageCount:pageCount];
for (auto& pageInfo : _pages)
[pageInfo.view removeFromSuperview];
_pages.clear();
_pages.reserveCapacity(pageCount);
CGRect pageFrame = CGRectMake(0, 0, _minimumSize.width, _minimumSize.height);
for (NSUInteger pageNumber = 0; pageNumber < pageCount; ++pageNumber) {
UIPDFPage *page = [_pdfDocument pageAtIndex:pageNumber];
if (!page)
continue;
CGSize pageSize = [page size];
pageFrame.size.height = pageSize.height / pageSize.width * pageFrame.size.width;
CGRect pageFrameWithMarginApplied = CGRectInset(pageFrame, pdfPageMargin, pdfPageMargin);
PDFPageInfo pageInfo;
pageInfo.page = page;
pageInfo.frame = pageFrameWithMarginApplied;
_pages.append(pageInfo);
pageFrame.origin.y += pageFrame.size.height - pdfPageMargin;
}
_documentFrame = [self frame];
_documentFrame.size.width = _minimumSize.width;
_documentFrame.size.height = std::max(pageFrame.origin.y + pdfPageMargin, _minimumSize.height);
[self setFrame:_documentFrame];
[_scrollView setContentSize:_documentFrame.size];
}
@end
#endif /* PLATFORM(IOS) */