Blog Archives
View PDF in Objective-C Cocoa on Mac OS X using PDFKit
The code below has been tested and works clean on the latest version of Mac OS X 10.5 Leopard.
Since it applies to native Mac OS X, and not iOS, porting this to work on iPhone/iPad is a bit different, however, it is overall very easy once you have the base concept and the main functionality below is the same.
Note: If you create this as a blank new project and are new to PDFKit, first make sure to add the Quartz framework to your project. (ref)
First, create a class (and corresponding header) to utilize the PDF. (CODE IS CASE SENSITIVE)
PDFImageView.h
#import <Cocoa/Cocoa.h> #import <Quartz/Quartz.h> @interface PDFImageView : NSImageView - (void) loadFromPath: (NSString *) path; @end
PDFImageView.m
#import "PDFImageView.h" #import "DraggableScrollView.h" @implementation PDFImageView - (NSPDFImageRep *) pdfRep { return [[[self image] representations] lastObject]; } - (void) loadFromPath: (NSString *)path { NSPDFImageRep *pdfRep; NSImage *pdfImage; NSRect frame; pdfRep = [NSPDFImageRep imageRepWithContentsOfFile:path]; pdfImage = [[[NSImage alloc] init]autorelease]; [pdfImage addRepresentation: pdfRep]; frame = [pdfRep bounds]; frame.size.height *= [pdfRep pageCount]; [self setImage: pdfImage]; [super setFrame: frame]; if ([self isFlipped]) [self scrollPoint: NSMakePoint(0,0)]; else { [self scrollPoint: NSMakePoint(0,frame.size.height)]; } } - (void) drawRect: (NSRect) rect { NSPDFImageRep *rep; int pageCount, pageNumber; NSRect onePageBounds; [[NSColor whiteColor] set]; NSRectFill (rect); rep = [self pdfRep]; pageCount = [rep pageCount]; for (pageNumber=0;pageNumber<pageCount;pageNumber++) { onePageBounds = [self rectForPage: (1+pageNumber)]; if (! NSIntersectsRect(rect, onePageBounds)) continue; [rep setCurrentPage: pageNumber]; [rep drawInRect: onePageBounds]; } } - (void) mouseDown: (NSEvent *) theEvent { NSScrollView *scrollView; scrollView=[self enclosingScrollView]; if ([scrollView respondsToSelector: @selector(dragDocumentWithMouseDown:)]) [(DraggableScrollView*)scrollView dragDocumentWithMouseDown: theEvent]; else { [super mouseDown: theEvent]; } - (void) setFrameSize: (NSSize) newSize { NSSize PDFsize; float correctHeight; PDFsize = [[self pdfRep] bounds].size; correctHeight = [[self pdfRep] pageCount] * (PDfsize.height/PDFsize.width) * newSize.width; correctHeight = ceil(correctHeight); if (abs (correctHeight - newSize.height) > 3.0) newSize.height = correctHeight; [super setFrameSize: newSize]; } - (BOOL) knowsPageRange: (NSRangePointer) range { range->location=1; range->length=[[self pdfRep] pageCount]; return YES; } - (NSRect) rectForPage: (int) pageNumber { NSPDFImageRep *rep; int pageCount; NSRect result; rep = [self pdfRep]; pageCount = [rep PageCount]; result = [rep bounds]; if (! [self isFlipped]) result = NSOffsetRect(result,0.0,(pageCount-1)*result.size.height); if ([self isFlipped]) result = NSOffsetRect (result,0.0,(pageNumber-1)*result.size.height); else { result = NSOffsetRect (result,0.0,-(pageNumber-1)*result.size.height); return result; } } @end
This class is courtesy of Apple DC and used in the above PDFViewer class. You shouldn’t have to really do anything additional in this one, just plug and play. Allows user to scroll with mouse. I removed alot of comments for brevity to minimize code lines. See Apple reference at bottom for full source and additional info.
DraggableScrollView.h
#import <Cocoa/Cocoa.h> @interface DraggableScrollView : NSScrollView - (BOOL) dragDocumentWithMouseDown: (NSEvent *) theEvent; @end
DraggableScrollView.m
#import "DraggableScrollView.h" @implementation DraggableScrollView #pragma mark PRIVATE CLASS METHODS + (NSCursor *) dragCursor { static NSCursor *openHandCursor = nil; if (openHandCursor == nil) { NSImage *image; image = [NSImage imageNamed: @"fingerCursor"]; openHandCursor = [[NSCursor alloc] initWithImage: image hotSpot: NSMakePoint (8, 8)]; // guess that the center is good } return openHandCursor; } #pragma mark PRIVATE INSTANCE METHODS - (BOOL) canScroll { if ([[self documentView] frame].size.height > [self documentVisibleRect].size.height) return YES; if ([[self documentView] frame].size.width > [self documentVisibleRect].size.width) return YES; return NO; } #pragma mark PUBLIC INSTANCE METHODS -- OVERRIDES FROM NSScrolLView - (void) tile { [super tile]; // If the user can scroll right now, make our document cursor reflect that. if ([self canScroll]) [self setDocumentCursor: [[self class] dragCursor]]; else [self setDocumentCursor: [NSCursor arrowCursor]]; } #pragma mark PUBLIC INSTANCE METHODS // dragDocumentWithMouseDown: -- Given a mousedown event, which should be in // our document view, track the mouse to let the user drag the document. - (BOOL) dragDocumentWithMouseDown: (NSEvent *) theEvent // RETURN: YES => user dragged (not clicked) { NSPoint initialLocation; NSRect visibleRect; BOOL keepGoing; BOOL result = NO; initialLocation = [theEvent locationInWindow]; visibleRect = [[self documentView] visibleRect]; keepGoing = YES; while (keepGoing) { theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask | NSLeftMouseDraggedMask]; switch ([theEvent type]) { case NSLeftMouseDragged: { NSPoint newLocation; NSRect newVisibleRect; float xDelta, yDelta; newLocation = [theEvent locationInWindow]; xDelta = initialLocation.x - newLocation.x; yDelta = initialLocation.y - newLocation.y; // This was an amusing bug: without checking for flipped, // you could drag up, and the document would sometimes move down! if ([[self documentView] isFlipped]) yDelta = -yDelta; // If they drag MORE than one pixel, consider it a drag if ( (abs (xDelta) > 1) || (abs (yDelta) > 1) ) result = YES; newVisibleRect = NSOffsetRect (visibleRect, xDelta, yDelta); [[self documentView] scrollRectToVisible: newVisibleRect]; } break; case NSLeftMouseUp: keepGoing = NO; break; default: /* Ignore any other kind of event. */ break; } // end of switch (event type) } // end of mouse-tracking loop return result; } @end
And now for the main class of our application which will use the above PDFImageView class and bind/map to our interface.
MainView.h
#import <Cocoa/Cocoa.h> #import <Quartz/Quartz.h> @interface MainView : NSDocument { IBOutlet PDFView *_pdfView; } @end
MainView.m
#import "MainView.h" @implementation MainView //will load PDF from local filesystem in current path as .app is launched from -(void)awakeFromNib{ NSFileManager *filemgr; filemgr = [[NsFileManager alloc]init]; NSString *currentpath = [[[[NSBundle mainBundle] bundlePath] stringByDeletingPathExtension] stringByDeletingLastPathcomponent]; //[Util MessageBox:@"currentpath":currentpath]; //SEE MY REFERENCE BELOW FOR DEBUG UTILITY CLASS NSString *fileName = [NSString stringWithFormat:@"%@/%@/",currentpath,@"filename.pdf"]; PDFDocument *pdfDoc = [[PDFDocument alloc] initWithURL:[NSURL fileURLWithPath:fileName]]; [_pdfView setDocument: pdfDoc]; } @end
References
Apple DC, http://developer.apple.com/library/mac/#samplecode/PDFView/Introduction/Intro.html
“Show Dialog Message Box in Objective-C Cocoa”, https://ronniediaz.com/2011/06/13/show-dialog-message-box-in-objective-c-cocoa/
Google CodeSearch (“_pdfView”), http://www.google.com/codesearch#search&q=_pdfView+lang:objectivec
Mac Sudo Password
Note: “Sudo” is short for “Substitute User Do” or “Superuser Do”
This may come as a shock if you’re new to Mac and ever used a *nix system before.. but the sudo password, is your password! If you’re administrator anyway, if not, ask your *rents, instructor or local admin to help you out. 😉
/etc/sudoers contains the permissions for who can use it, and once you have it, it is very powerful indeed.
If for any reason you need to have access to the full root account, this is actually not enabled on a Mac by default, for good reason.
To Enable Root User (see Apple KB below for reference):
1) From the Apple menu choose System Preferences….
2) From the View menu choose Accounts.
3) Click on the lock and authenticate with an administrator account.
4) Click Login Options….
5) Click the “Edit…” or “Join…” button at the bottom right.
6) Click the “Open Directory Utility…” button.
7) Click the lock in the Directory Utility window.
8) Enter an administrator account name and password, then click OK.
9) Choose Enable Root User from the Edit menu.
10) Enter the root password you wish to use in both the Password and Verify fields, then click OK.
References
Apple KBase, http://support.apple.com/kb/ht1528
Wikipedia (Sudo), http://en.wikipedia.org/wiki/Sudo
Wikipedia (SU), http://en.wikipedia.org/wiki/Su_%28Unix%29
vi keyboard shortcuts Quick Reference
Never know when those quick reference bookmarks might point to dead links, usually when you need it most!
For this reason, I’ve consolidated a quick list from the link below and removed ads and unwanted formatting for a little cleaner html table (no offense keyxl) and easier copying by you. For your future reference, and mine. 😉
Shortcut | Command | |
Insert
|
||
i | Inserts text to the left of the cursor. | |
I | Inserts text at the beginning of the line, no matter where the cursor is positioned on the current line. |
Append
|
|||
a | Begins inserting after the character (append) on which the cursor is positioned. | ||
A | Begins inserting at the end of the current line, no matter where the cursor is positioned on that line. |
Open
|
|||
o | Begins inserting text on a new, empty line that is opened for you, below the current line. This is the only command that will allow you to insert text BELOW the LAST line of the file. | ||
O | Begins inserting text on a new, empty line that is opened for you, above the current line. This is the only command that will allow you to insert text ABOVE the FIRST line of the file. |
Deleting,copying and changing
|
|||
d | Delete text. (see explanation above) | ||
y | Copy text (that is, yank it into a holding area for later use). (see explanation above) | ||
c | Change text from one thing to another, which you will type. (see explanation above) | ||
! | Filter text through a program. | ||
< | Shift a region of text to the left. | ||
> | Shift a region of text to the right. |
Single Key Movements
|
|||
h | Move cursor to the left one character. | ||
l | Move cursor to the right one character. | ||
j | Move cursor down one line. | ||
k | Move cursor up one line. | ||
^ | Move cursor to the beginning of the line. | ||
$ | Move cursor to the end of the current line. | ||
1G | Move cursor to the first line of your document. Other numbers will move to the line specified by number (ex. 50G goes to the 50th line). | ||
G | Move cursor to the last line of your file. | ||
CTRL U | Move cursor up in file 12 lines. Hold down the key marked CTRL (stands for control) and type U. CTRL is like another shift key. | ||
CTRL D | Move cursor down in file 15 lines. | ||
w | Move cursor forward to the next word, stopping at punctuation. | ||
W | Move cursor forward to the next word, ignoring punctuation. | ||
e | Move cursor forward to the end of the word, stopping at punctuation. | ||
E | Move cursor forward to the end of the word, ignores punctuation. | ||
b | Move cursor backwards to the previous word, stopping at punctuation. | ||
B | Move cursor backwards to the previous word, ignores punctuation. | ||
H | Move cursor to the top line of the screen, (as opposed to the top of the document which may not be the same place). | ||
M | Move cursor to the middle of the screen. | ||
L | Move cursor to the last line on the screen. | ||
% | Move cursor to the matching parenthesis, bracket or brace. Great for debugging programs. | ||
( | Move cursor to the beginning of the previous sentence (where a punctuation mark and two spaces define a sentence). | ||
) | Move cursor to the beginning of the next sentence. | ||
{ | Move cursor to the beginning of the current paragraph. | ||
} | Move cursor to the beginning of the next paragraph. | ||
; | Repeat the last f or F command (see below). |
Almost Single Key Movements
|
|||
‘ | Move cursor to a previously marked location in the file. (ex. ma marks the location with the letter a, so a (apostrophe a) moves back to that location). | ||
f | Find the character corresponding to the next keystroke typed. Move the cursor to the next occurrence of that character (on the current line only). | ||
F | Same as f but movement is backwards. |
Useful
|
|||
x | Delete character(s) to the right of the cursor, starting with the one beneath it. | ||
r | Replace the character under the cursor with the next character you type. This can be a very useful command. If you wanted to split up a line between two words, you might put the cursor on the blank space before the word you would like to go on the next line and type r . This would replace the space between the words with a carriage return and put the rest of the line onto a new line. | ||
J | Join lines; the opposite of the line splitting operation above. This will join the current line with the next line in your file. Also very useful. | ||
R | Replace lines; puts you in INSERT mode but types over the characters that are already on the current line. | ||
p | Paste line(s) you deleted (or yanked) back into the file. This is an excellent command if you want to move a few lines somewhere else in your file. Just type 3dd to delete three lines, for example, and then move to where you want those lines to be and type p to paste the lines back into your file below the cursor. | ||
. | The period . command repeats the last text modification command, whatever it may have been (insert, deletion, etc). | ||
:r filename RETURN | Read a file into the current file being edited. The file be added gets placed below the current cursor position. Please note the colon : before the r in this command. | ||
CTRL L | Redraw the screen. If somebody writes to you while you are in the middle of vi and junk appears all over your screen, dont panic, it did not hurt your file, but you will have to hold down the CTRL key and type L to clean it up (CTRL L). | ||
d$ | Delete (including the current character), to the end of the line. | ||
d^ | Delete (excluding the current character), to the beginning of the line. | ||
dw | |||
dW | Delete a word(s), ignoring punctuation. | ||
de | Delete to the end of next word. | ||
dd | Delete a line(s). | ||
dG | Delete from the current line to the end of the document. CAREFUL: Slightly dangerous. | ||
dH | Delete from the current line to the line shown at the top of the screen. |
Search and Replace
|
|||
/the | Finds the next occurence of the. This will also find their, them, another, etc. | ||
?the | Finds the previous occurence of the. | ||
n | Repeats the last search command. Finds the Next occurence. | ||
d/the | Deletes until the next occurence of the. This is to demonstrate how the delete prefix can be used with any cursor movement command. | ||
:g/oldword/s//newword/gc | This will find all occurences of oldword and replace them with newword. The optional c at the end of the command tells vi that you would like to confirm each change. Vi will want you to type in y to make the change or n to skip that replacement. Great for spelling fixes. |
Exit
|
|||
ESC :wq RETURN | Save and exit VI | ||
ESC :q! RETURN | Exit WITHOUT saving changes |
References
keyxl.com, http://www.keyxl.com/aaab462/105/VIM-Text-Editor-keyboard-shortcuts.htm
Mac OS X Quick Reference
After writing multiple articles on a single subject, I decided to consolidate the links into a single point of reference. All links below are internal to my site and do not redirect outside of my blog.
(Tested on 10.6 Leopard.)
enable root user, or mac sudo password and settings, https://ronniediaz.com/2011/04/14/mac-sudo-password/
install macports and/or issues with install and configuring, https://ronniediaz.com/2011/04/14/no-indexes-found-sync-your-source-ports
converting iso images with bchunk, (discussed in my article on creating hybrid discs with toast), https://ronniediaz.com/2011/04/14/convert-toast-bin-to-iso-in-mac-os-x
open textedit from command line, https://ronniediaz.com/2011/04/14/open-textedit-from-command-line-on-mac-os-x-10-6-leopard/
vi keyboard shortcuts Quick Reference, https://ronniediaz.com/2011/04/14/vi-keyboard-shortcuts-quick-reference/
Open TextEdit from command line on Mac OS X 10.6 Leopard
I found other articles online which mention editing ~/.bash.rc and ~./bash_profile but these are more work than necessary.
Simply use:
open -e [filename]
However, I noticed this functionality has some limitations concerning the files edited.
After trying the following commands on a secure file, it still would not save after making changes.
sudo open -e [filename] sudo chmod 777 [filename] sudo open -e [filename] sudo chown -R [currentusershortname]:staff [filename] sudo open -e [filename] su open -e [filename]
If you run into this issue where you cannot save after opening the file or any others with TextEdit, an awesome alternative and what I ultimately used is the tried and true vi.
See my “vi keyboard shortcuts quick reference” article for more info.
References
vi keyboard shortcuts quick reference, https://ronniediaz.com/2011/04/14/vi-keyboard-shortcuts-quick-reference