Mac OS X dev小笔记【最新:issue6 PDF目录解析问题】

issue 1 How do I make an OS X application react when a file, picture, etc is dropped on its dock icon?


http://stackoverflow.com/questions/501079/how-do-i-make-an-os-x-application-react-when-a-file-picture-etc-is-dropped-on


Some applications, like Photoshop, allow users to drag a picture from a web browser, or drag a file from the filesystem, onto the application's icon in the dock. Doing this opens the file in that application.

How is this done? I'd like to use Cocoa and Objective-C, but I'm interested in any solutions in any languages.



NSApplication allows you to set a delegate for your application. If the user drags a file onto your dock icon, NSApplication will call the method

- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename

of your delegate object, in case it implements any such method. In case the content is not really a file (e.g. if the user just selects text in an application and drags it onto your dock icon), the delegate method

- (BOOL)applicationOpenUntitledFile:(NSApplication *)theApplication

is called.

See NSApplication class reference

Basically you can just create any object of any kind (e.g. a simple one that just inherits of NSObject), define the two methods of above within the object and then anywhere in your start up code of the app you do

whatever = [[YourObject alloc] init];
[[NSApplication sharedApplication] setDelegate:whatever];

And that's it. As soon as a file or some other content is dropped onto the dock icon, the appropriate method is called and must handle that request. BTW the same methods are called if your application associates with a file type (e.g. .myFileType) and the user double clicks a file with that extension in the Finder.

What really happens behind the scenes is that Launch Services sends your application an "open documents" ('odoc') Apple Event. NSApplication by default registers a handle for this event and forwards the request by calling the appropriate delegate method. You can also directly listen to this Apple Event I guess, but why would you? Dealing with Apple Events directly is awkward. When your application is not Cocoa, but Carbon (plain-C), you may have to directly process the Apple Event (I'm not familiar with Carbon), but in Cocoa Apple already catches the most important Apple Events for you and converts them into delegate calls or notifications your application can listen to.



2 down vote

If your application is document-based and you filled out the necessary keys in your Info.plist correctly, then it Just Works. When the user drags a file to your application's Dock tile, Dock will highlight your app on the tile if the file is of a type you signed up for, and if the user drops the file there, NSDocumentController will instantiate one of your document classes for the file. If the file is not of a type you signed up for, both will ignore it.

So, is your app document-based? If so, is the file one of a type you signed up for?

For more information:

  • Document-Based App Programming Guide for Mac
  • Information Property List Key Reference
  • How to make your app's Dock tile highlight [if it's supposed to but doesn't]

issue 2 怎么没有NSLabel?

There is no label class (NSLabel) on OS X. You have to use NSTextField instead, remove the bezel and make it non editable:

[textField setBezeled:NO];
[textField setDrawsBackground:NO];
[textField setEditable:NO];
[textField setSelectable:NO];


issue3 NSView 变个背景颜色这么难?

NSView doesn't have the concept of a background color, because it
doesn't do any drawing. You'll have to do your drawing in your
override of -drawRect:.Or use an NSBox set to a custom color.


issue4 NSTextField 圆角化

-(void)awakeFromNib {

    [[self cell] setBezelStyle: NSTextFieldRoundedBezel];
}


- (void)drawRect:(NSRect)dirtyRect
{

    NSRect blackOutlineFrame = NSMakeRect(0.0, 0.0, [self bounds].size.width, [self bounds].size.height-1.0);
    NSGradient *gradient = nil;
    if ([NSApp isActive]) {
        gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:0.24 alpha:1.0] endingColor:[NSColor colorWithCalibratedWhite:0.374 alpha:1.0]];
    }
    else {
        gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:0.55 alpha:1.0] endingColor:[NSColor colorWithCalibratedWhite:0.558 alpha:1.0]];
    }

    [gradient drawInBezierPath:[NSBezierPath bezierPathWithRoundedRect:blackOutlineFrame xRadius:10 yRadius:10] angle:90];

}

issue5 编码问题

一篇介绍编码不错的文章http://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html

一个分析文本编码的工具enca  http://linux.die.net/man/1/enca

源码https://github.com/nijel/enca/

sh-3.2# enca eng.srt
Universal character set 2 bytes; UCS-2; BMP
  CRLF line terminators
  Byte order reversed in pairs (1,2 -> 2,1)

对照说明,应该是utf-16。成功读取该字幕文件

issue6 PDF目录解析问题

        在OSX 和IOS中遇到需要解析PDF目录时,我们可以直接使用PDF Kit提供的PDFOutline class来进行解析,这个类和xml parser非常相似,都是基于孩子存储的树。配合NSOutlineView可以非常容易地显示出整个目录来。如果不使用该类的话,那只能通过CGPDFDocument里的一些方法来遍历解析整个catalog树,该树采用孩子兄弟形式存储,似乎就不大好处理了。而且我们对于一个PDF的catalog了解还不够多,可能无法适应各种情况。GitHub上有个解析的demo,PDFOutlineParser。我使用了时候发现有些bug,譬如说我自己制作的书签会含有“Dest”键的字典,但是他只支持“D”键的字典。他的思路还是比较清晰的,采用了递归的方法,每次取当前节点信息,然后分别取first child节点、next brother节点的信息。每页page,分别问题是不好处理结果,母标签和子标签混在一起区分不出来。解析的思路:

首先要考虑是否有name对象,name对象是page 的迂回对象和页名组成的键值对字典(数组)。

Mac OS X dev小笔记【最新:issue6 PDF目录解析问题】_第1张图片

这边的string是页的名字,一般是chapter和page两种,dictionary则是该页的迂回对象。考虑到每页的迂回对象的地址都是不同的,于是建立起了页码和页对象(可以把迂回对象当做该页本身)地址的映射关系。对于某个chapter,也就是标签指向的页,可以通过比较该chapter的页对象的地址与哪页的页对象地址相同来获取。而某个chapter的名字,也就是目录的某条内容,则需要从outlines对象里的映射关系中获取。

Mac OS X dev小笔记【最新:issue6 PDF目录解析问题】_第2张图片



对于没有名字的情况

Mac OS X dev小笔记【最新:issue6 PDF目录解析问题】_第3张图片

我们首先可以从page中获取每页页码以及迂回对象的对应关系。

Mac OS X dev小笔记【最新:issue6 PDF目录解析问题】_第4张图片

然后再从outline中获取迂回对象和标签内容的对应关系。从而获得标签与页码的对应关系。


再补充一些,解析目录时,我们要区分是否有name。然后还要区分是Action还是Dest。

当然Dest的类型和有无name是有关联的:

The /Dest entry in an outline item dictionary can either be a name, a string, or an array.

  • The simplest case is if it's an array; then the first item is the page object the outline entry points to (a dictionary). To get the page number, you have to iterate over all pages in the document and see which one is equal (==) to the dictionary you have (CGPDFPageRefs are actually CGPDFDictionaryRefs). You could also traverse the page tree, which is a bit harder, but may be faster (not as much as you might expect, I wouldn't optimize prematurely here). The other items in the array are position on the page etc., search for "Explicit Destinations" in the PDF spec to learn more.

  • If the entry is a name or string, it is a named destination. You have to map the name to a destination from the document catalog's/Dests entry which is a dictionary that contains a name tree. A name tree is essentially a tree map that allows fast access to named values without requiring to read all the data at once (as with a plain dictionary). Unfortunately, there's no direct support for name trees in Quartz, so you'll have to do a little more work to parse this structure recursively (see "Name Trees" in thePDF spec).

Note that an outline item doesn't necessarily have a /Dest entry, it can also specify its destination via an/A (action) entry, which is a little bit more complex. In most cases, however, the action will be a "GoTo" action that is essentially a wrapper for a destination.

The mapping of names to destinations can also be stored as a plain dictionary. In that case, it's in the/Dests entry of the /Names dictionary in the document's catalog. I've rarely seen this though and it was deprecated after PDF 1.2 (current is 1.7).

You will definitely need the PDF spec for this: http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf

也就是说当没有name的时候,Dest是数组类型的;而当有name时候,Dest是name或者string类型。而Dests的位置也存在不确定性,可能存在于catalog/Dests,也可能存在于catalog/Names/Dests。这边要区分以下Names和Dests。Names是名字字典,也即是PDF中一些可命名对象与名字的映射关系。而Dests是其中一种可命名对象的名字树,这种对象是destination。注意:上面引用的一段话,可能存在错误,PDF 1.2之前,Dests一般直接在catalog下面;而1.2以后可以防止Names下面。为此你可以查看文档说明《12.3.2.3 Named Destinations

出于性能的考虑,pages树一般构造为平衡树。因此里面的节点可能是page,也可能是pages。

你可能感兴趣的:(objective-c,Mac开发)