📄 webpdfview.mm
字号:
if ([unwantedActions containsObject:actionString]) continue; // Copy items since a menu item can be in only one menu at a time, and we don't // want to modify the original menu supplied by PDFKit. NSMenuItem *itemCopy = [item copy]; [copiedItems addObject:itemCopy]; // Include all of PDFKit's separators for now. At the end we'll remove any ones that were made // useless by removing PDFKit's menu items. if ([itemCopy isSeparatorItem]) continue; NSNumber *tagNumber = [actionsToTags objectForKey:actionString]; int tag; if (tagNumber != nil) tag = [tagNumber intValue]; else { // This should happen only if PDFKit updates behind WebKit's back. It's non-ideal because clients that only include tags // that they recognize (like Safari) won't get these PDFKit additions until WebKit is updated to match. tag = WebMenuItemTagOther; LOG_ERROR("no WebKit menu item tag found for PDF context menu item action \"%@\", using WebMenuItemTagOther", actionString); } if ([itemCopy tag] == 0) { [itemCopy setTag:tag]; if ([itemCopy target] == PDFSubview) { // Note that updating the defaults is cheap because it catches redundant settings, so installing // the proxy for actions that don't impact the defaults is OK [itemCopy setTarget:PDFSubviewProxy]; } } else LOG_ERROR("PDF context menu item %@ came with tag %d, so no WebKit tag was applied. This could mean that the item doesn't appear in clients such as Safari.", [itemCopy title], [itemCopy tag]); } [actionsToTags release]; [unwantedActions release]; // Since we might have removed elements supplied by PDFKit, and we want to minimize our hardwired // knowledge of the order and arrangement of PDFKit's menu items, we need to remove any bogus // separators that were left behind. [copiedItems _webkit_removeUselessMenuItemSeparators]; return copiedItems;}- (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection{ if (![string length]) return nil; int options = 0; if (!forward) options |= NSBackwardsSearch; if (!caseFlag) options |= NSCaseInsensitiveSearch; PDFDocument *document = [PDFSubview document]; PDFSelection *selectionForInitialSearch = [initialSelection copy]; if (startInSelection) { // Initially we want to include the selected text in the search. PDFDocument's API always searches from just // past the passed-in selection, so we need to pass a selection that's modified appropriately. // FIXME 4182863: Ideally we'd use a zero-length selection at the edge of the current selection, but zero-length // selections don't work in PDFDocument. So instead we make a one-length selection just before or after the // current selection, which works for our purposes even when the current selection is at an edge of the // document. int initialSelectionLength = [[initialSelection string] length]; if (forward) { [selectionForInitialSearch extendSelectionAtStart:1]; [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength]; } else { [selectionForInitialSearch extendSelectionAtEnd:1]; [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength]; } } PDFSelection *foundSelection = [document findString:string fromSelection:selectionForInitialSearch withOptions:options]; [selectionForInitialSearch release]; // If we first searched in the selection, and we found the selection, search again from just past the selection if (startInSelection && _PDFSelectionsAreEqual(foundSelection, initialSelection)) foundSelection = [document findString:string fromSelection:initialSelection withOptions:options]; if (!foundSelection && wrapFlag) foundSelection = [document findString:string fromSelection:nil withOptions:options]; return foundSelection;}- (void)_openWithFinder:(id)sender{ // We don't want to write the file until we have a document to write (see 4892525). if (![PDFSubview document]) { NSBeep(); return; } NSString *opath = [self _path]; if (opath) { if (!written) { // Create a PDF file with the minimal permissions (only accessible to the current user, see 4145714) NSNumber *permissions = [[NSNumber alloc] initWithInt:S_IRUSR]; NSDictionary *fileAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:permissions, NSFilePosixPermissions, nil]; [permissions release]; [[NSFileManager defaultManager] createFileAtPath:opath contents:[dataSource data] attributes:fileAttributes]; [fileAttributes release]; written = YES; } if (![[NSWorkspace sharedWorkspace] openFile:opath]) { // NSWorkspace couldn't open file. Do we need an alert // here? We ignore the error elsewhere. } }}- (NSString *)_path{ // Generate path once. if (path) return path; NSString *filename = [[dataSource response] suggestedFilename]; NSFileManager *manager = [NSFileManager defaultManager]; NSString *temporaryPDFDirectoryPath = [self _temporaryPDFDirectoryPath]; if (!temporaryPDFDirectoryPath) { // This should never happen; if it does we'll fail silently on non-debug builds. ASSERT_NOT_REACHED(); return nil; } path = [temporaryPDFDirectoryPath stringByAppendingPathComponent:filename]; if ([manager fileExistsAtPath:path]) { NSString *pathTemplatePrefix = [temporaryPDFDirectoryPath stringByAppendingPathComponent:@"XXXXXX-"]; NSString *pathTemplate = [pathTemplatePrefix stringByAppendingString:filename]; // fileSystemRepresentation returns a const char *; copy it into a char * so we can modify it safely char *cPath = strdup([pathTemplate fileSystemRepresentation]); int fd = mkstemps(cPath, strlen(cPath) - strlen([pathTemplatePrefix fileSystemRepresentation]) + 1); if (fd < 0) { // Couldn't create a temporary file! Should never happen; if it does we'll fail silently on non-debug builds. ASSERT_NOT_REACHED(); path = nil; } else { close(fd); path = [manager stringWithFileSystemRepresentation:cPath length:strlen(cPath)]; } free(cPath); } [path retain]; return path;}- (void)_PDFDocumentViewMightHaveScrolled:(NSNotification *)notification{ NSClipView *clipView = [self _clipViewForPDFDocumentView]; ASSERT([notification object] == clipView); NSPoint scrollPosition = [clipView bounds].origin; if (NSEqualPoints(scrollPosition, lastScrollPosition)) return; lastScrollPosition = scrollPosition; WebView *webView = [self _webView]; [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[[dataSource webFrame] frameView]];}- (PDFView *)_PDFSubview{ return PDFSubview;}- (BOOL)_pointIsInSelection:(NSPoint)point{ PDFPage *page = [PDFSubview pageForPoint:point nearest:NO]; if (!page) return NO; NSRect selectionRect = [PDFSubview convertRect:[[PDFSubview currentSelection] boundsForPage:page] fromPage:page]; return NSPointInRect(point, selectionRect);}- (void)_scaleOrDisplayModeOrPageChanged:(NSNotification *)notification{ ASSERT([notification object] == PDFSubview); if (!_ignoreScaleAndDisplayModeAndPageNotifications) { [self _updatePreferencesSoon]; // Notify UI delegate that the entire page has been redrawn, since (unlike for WebHTMLView) // we can't hook into the drawing mechanism itself. This fixes 5337529. WebView *webView = [self _webView]; [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView bounds]]; }}- (NSAttributedString *)_scaledAttributedString:(NSAttributedString *)unscaledAttributedString{ if (!unscaledAttributedString) return nil; float scaleFactor = [PDFSubview scaleFactor]; if (scaleFactor == 1.0) return unscaledAttributedString; NSMutableAttributedString *result = [[unscaledAttributedString mutableCopy] autorelease]; unsigned int length = [result length]; NSRange effectiveRange = NSMakeRange(0,0); [result beginEditing]; while (NSMaxRange(effectiveRange) < length) { NSFont *unscaledFont = [result attribute:NSFontAttributeName atIndex:NSMaxRange(effectiveRange) effectiveRange:&effectiveRange]; if (!unscaledFont) { // FIXME: We can't scale the font if we don't know what it is. We should always know what it is, // but sometimes don't due to PDFKit issue 5089411. When that's addressed, we can remove this // early continue. LOG_ERROR("no font attribute found in range %@ for attributed string \"%@\" on page %@ (see radar 5089411)", NSStringFromRange(effectiveRange), result, [[dataSource request] URL]); continue; } NSFont *scaledFont = [NSFont fontWithName:[unscaledFont fontName] size:[unscaledFont pointSize]*scaleFactor]; [result addAttribute:NSFontAttributeName value:scaledFont range:effectiveRange]; } [result endEditing]; return result;}- (void)_setTextMatches:(NSArray *)array{ [array retain]; [textMatches release]; textMatches = array;}- (NSString *)_temporaryPDFDirectoryPath{ // Returns nil if the temporary PDF directory didn't exist and couldn't be created static NSString *_temporaryPDFDirectoryPath = nil; if (!_temporaryPDFDirectoryPath) { NSString *temporaryDirectoryTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPDFs-XXXXXX"]; char *cTemplate = strdup([temporaryDirectoryTemplate fileSystemRepresentation]); if (!mkdtemp(cTemplate)) { // This should never happen; if it does we'll fail silently on non-debug builds. ASSERT_NOT_REACHED(); } else { // cTemplate has now been modified to be the just-created directory name. This directory has 700 permissions, // so only the current user can add to it or view its contents. _temporaryPDFDirectoryPath = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:cTemplate length:strlen(cTemplate)] retain]; } free(cTemplate); } return _temporaryPDFDirectoryPath;}- (void)_trackFirstResponder{ ASSERT([self window]); id newFirstResponder = [[self window] firstResponder]; if (newFirstResponder == trackedFirstResponder) return; // This next clause is the entire purpose of _trackFirstResponder. In other WebDocument // view classes this is done in a resignFirstResponder override, but in this case the // first responder view is a PDFKit class that we can't subclass. if (trackedFirstResponder == [PDFSubview documentView] && ![[dataSource _webView] maintainsInactiveSelection]) [self deselectAll]; [trackedFirstResponder release]; trackedFirstResponder = [newFirstResponder retain];}- (void)_updatePreferences:(WebPreferences *)prefs{ float scaleFactor = [PDFSubview autoScales] ? 0.0f : [PDFSubview scaleFactor]; [prefs setPDFScaleFactor:scaleFactor]; [prefs setPDFDisplayMode:[PDFSubview displayMode]]; _willUpdatePreferencesSoon = NO; [prefs release]; [self release];}- (void)_updatePreferencesSoon{ // Consolidate calls; due to the PDFPrefUpdatingProxy method, this can be called multiple times with a single user action // such as showing the context menu. if (_willUpdatePreferencesSoon) return; WebPreferences *prefs = [[dataSource _webView] preferences]; [self retain]; [prefs retain]; [self performSelector:@selector(_updatePreferences:) withObject:prefs afterDelay:0]; _willUpdatePreferencesSoon = YES;}- (NSSet *)_visiblePDFPages{ // Returns the set of pages that are at least partly visible, used to avoid processing non-visible pages PDFDocument *pdfDocument = [PDFSubview document]; if (!pdfDocument) return nil; NSRect pdfViewBounds = [PDFSubview bounds]; PDFPage *topLeftPage = [PDFSubview pageForPoint:NSMakePoint(NSMinX(pdfViewBounds), NSMaxY(pdfViewBounds)) nearest:YES]; PDFPage *bottomRightPage = [PDFSubview pageForPoint:NSMakePoint(NSMaxX(pdfViewBounds), NSMinY(pdfViewBounds)) nearest:YES]; // only page-free documents should return nil for either of these two since we passed YES for nearest: if (!topLeftPage) { ASSERT(!bottomRightPage); return nil; } NSUInteger firstVisiblePageIndex = [pdfDocument indexForPage:topLeftPage]; NSUInteger lastVisiblePageIndex = [pdfDocument indexForPage:bottomRightPage]; if (firstVisiblePageIndex > lastVisiblePageIndex) { NSUInteger swap = firstVisiblePageIndex; firstVisiblePageIndex = lastVisiblePageIndex; lastVisiblePageIndex = swap; } NSMutableSet *result = [NSMutableSet set]; NSUInteger pageIndex; for (pageIndex = firstVisiblePageIndex; pageIndex <= lastVisiblePageIndex; ++pageIndex) [result addObject:[pdfDocument pageAtIndex:pageIndex]]; return result;}@end@implementation PDFPrefUpdatingProxy- (id)initWithView:(WebPDFView *)aView{ // No [super init], since we inherit from NSProxy view = aView; return self;}- (void)forwardInvocation:(NSInvocation *)invocation{ [invocation invokeWithTarget:[view _PDFSubview]]; [view _updatePreferencesSoon];}- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{ return [[view _PDFSubview] methodSignatureForSelector:sel];}@end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -