Search code examples
objective-ccocoaosx-mountain-lionosx-mavericksnspasteboard

NSPasteboard unable to read text when RTF data is copied to the pasteboard


I am using the NSPasteboard to read data off it and perform actions with the received data.

Specifically, I am interested in information of the types listed below in order of priority.

  1. File path
  2. Text data
  3. Image data (this is not copying the image file, but actual parts of the image, for e.g. when you open an image in an editor, make a selection and press command+v)

This is what I have in code

_pasteBoard = [NSPasteboard generalPasteboard];
NSArray *types = [_pasteBoard types];

NSArray *files = [_pasteBoard propertyListForType: NSFilenamesPboardType];

NSString *text = [_pasteBoard propertyListForType: NSStringPboardType];

NSString *text2 = [_pasteBoard propertyListForType: NSPasteboardTypeString];

NSString *rtfText = [_pasteBoard propertyListForType: NSRTFPboardType];

NSData *imageData = [_pasteBoard dataForType: NSTIFFPboardType];

Right now, I am simply testing if I am able to get the required data or not. So When I select some files on the Destkop then files contains the selected file URLs. So that works.

Similarly when I select some parts of an image and copy it to the clipboard then imageData contains some data, which I then write to another file and can see that only the selected potion was copied, so that is OK too. Also, I understand that when I copy an image "file" to the clipboard then too imageData will not be nil, but that is ok because topmost priority goes to file URL and that is the case that will do what needs to be done.

The problem that I have run into is with the lines related to reading text from the pasteboard.

NSString *text = [_pasteBoard propertyListForType: NSStringPboardType];

NSString *text2 = [_pasteBoard propertyListForType: NSPasteboardTypeString];

NSString *rtfText = [_pasteBoard propertyListForType: NSRTFPboardType];

The Problem Descriptio:

  1. I copied the URL of this page and both "text" and "text2" contain the string from the pasteboard. So it good here :)

  2. I copy some lines from a .cs code file opened in TextEdit, the clipboard shows that I have "public.utf8-plain-text" and "NSStringPboardType" but "text" and "text2" are both nil;

  3. From the same C#.NET .cs file, I select a single word, without any breaks for eg. "System.Threading", the clipboard shows that I have "public.utf8-plain-text" and "NSStringPboardType" and both "text" and "text2" have the copied text but any thing copied with spaces or lines will not. "myProject.Core.Helpers" will work but "namespace myProject.Core.Helpers" wont.

  4. Similarly, I copy some text from a RTF file, the clipboard shows that I have a lot of RTF related information along with the same old "public.utf8-plain-text" and "NSStringPboardType" types too, but again "text", "text2" and, this time, "rtfText" are all nil.

So, how do I get the text that I need in all cases from the clipboard? I know that RTF data can contain a lot more then text but I am just concerned with getting the text out in a string file.

Any help will be greatly appreciated.

Thanks!


Solution

  • The problem is almost certainly that you're not accounting for multiple items on the pasteboard. In the old days (OS X 10.5 and earlier), there was only one item on the pasteboard. That item could have multiple types, which were just supposed to be representations of the same thing.

    Now, the pasteboard can have multiple items. Each item can still have multiple types. The -types method returns a union of the types of all of the items. But -propertyListForType: will only examine the first item.

    You could iterate over the array returned by -pasteboardItems and ask each for its types and then get the property list object for each type.

    Or you could, for each type you're interested in, invoke -readObjectsForClasses:options: with a single-element array of the corresponding class. That wouldn't (and you generally shouldn't) distinguish among different plain text types. All of those would match NSString.

    The most common approach, though, is to construct an array of all classes you can handle, in order of your app's preference (usually most expressive to least), and make a single call to -readObjectsForClasses:options:. Then process all objects returned. Each object will be for a single item on the pasteboard in the "best" available form.