views:

382

answers:

3

When I put formatted text into the pasteboard (NSRTFPboardType), it pastes with all formatting preserved. But what I'd really like is to discard the font face and size information, while preserving the weight, color, etc.

According to the docs, an NSAttributedString with no font information will default to Helvetica 12, so that seems like a dead end.

I can also generate the text on demand, so if I could find out the font in the current UI element, I could modify the text before it goes into the pasteboard. I was hoping the accessibility API could help with this, but none of the attributes I can find in the UIElementInspector seem to deal with formatting.

Any ideas?

Here's a test case. It pastes in Helvetica 12 even though the only attribute is a green color:

// Create the string with no attributes, and strip the font just in case.
NSMutableAttributedString *s = [[[NSMutableAttributedString alloc] initWithString:@"Hello green world!"] autorelease];
[s removeAttribute:NSFontAttributeName range:NSMakeRange(0, [s length])];

// Add a test attribute
[s addAttribute:NSForegroundColorAttributeName value:[NSColor greenColor] range:NSMakeRange(6, 5)];

// Generate RTF data 
NSData *rtf = [s RTFFromRange:NSMakeRange(0, [s length]) documentAttributes:nil];

// Copy to pasteboard
NSPasteboard *pb = [NSPasteboard generalPasteboard];
[pb declareTypes:[NSArray arrayWithObject:NSRTFPboardType] owner:nil];
[pb setData:rtf forType:NSRTFPboardType];

Here's something interesting. If I try and generate the plainest raw RTF data I can, with absolutely no font information, it still pastes in Helvetica 12!

char *rawrtf = "{\\rtf1\\ansi\\ansicpg1252\n"
               "Hello world.}";
NSData *rtf = [NSData dataWithBytes:rawrtf length:strlen(rawrtf)];

So if this is possible at all, I think it's only possible by querying the currently running application about the current font.

A: 

NSAttributedString is immutable. I think perhaps what you need is an NSMutableAttributedString. This guide has an example of how to change things. You should be able to create a new NSMutableAttributedString as a copy of the NSAttributedString, and then make your modifications to it before passing it on to the pasteboard.

EDIT:

I'm not quite sure of what you what to do. You are saying that you want to preserve some formatting, and remove other formatting, and it seems like you understand how to manipulate the NSMutableAttributedString attributes, so what's the problem? Create a new attributed string, interrogate the old one, and apply whatever attributes you want to the new one, if you don't like the default font, change it. I'm not understanding where the problem is in this.

EDIT2:

Using initWithAttributedString: should copy the font properties over, but if it doesn't you should be able to use enumerateAttributesInRange:options:usingBlock: to interrogate your original NSAttributedString

EDIT3:

NSAttributedString *attrStr;
NSRange limitRange;
NSRange effectiveRange;
id attributeValue;

limitRange = NSMakeRange(0, [attrStr length]);

while (limitRange.length > 0) {
    attributeValue = [attrStr attribute:NSFontAttributeName
        atIndex:limitRange.location longestEffectiveRange:&effectiveRange
        inRange:limitRange];

    // apply attributeValue to the other mutable attributed string range here

    limitRange = NSMakeRange(NSMaxRange(effectiveRange),
        NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
}
slf
I've tried generating a new NSMutableAttributedString with no formatting, and stripping out the font attributes. It still pastes as Helvetica 12.
kvance
[mutableAttrStr addAttribute:NSFontAttributeName value:nameOfFont range:r] should work
slf
The problem is that I don't know nameOfFont. If I could determine the current font for what the user is editing, I could set it.The user is generally going to be pasting outside of my application, and I'm not sure if it's possible to access the current font outside my own app. That's why I was looking into the accessibility API.
kvance
As an example, say the user is composing a message in Mail.app and the current font is Times New Roman 14. When he pastes in rich text, it's in Helvetica 12, the wrong font.I'm looking for a way to keep it in Times New Roman 14, while still preserving the bold, italics, colors, etc.
kvance
Yeah, what he's saying is: Set the font yourself, explicitly, based on the font of the surrounding text.
Wevah
Oh, well, in that case, it's not your problem. Have you tested it out to see if it does in fact paste with Helvetica 12, or instead uses the font of the surrounding text?
Wevah
I have indeed tested it. And while it's technically not my problem, my users are complaining about it so it's de facto my problem ;)
kvance
EDIT 3 looked promising, but still no joy. I think the Helvetica 12 is being added during RTFFromRange:
kvance
well, you can't do everything for your users. i would expect text i copy from one place to look like where it came from when i past it to another place.
pxl
Mail's editing pane is built from a WebView so it may well prefer HTML during a drop. Would be interesting to test it at least.
Mike Abdullah
A: 

changing copied text so it'll be different from what the user had copied would go against the idea of copy and paste, no?

pxl
In this case, it's not something the user has copied. It's formatted text that's being generated by my app which the user would like to paste in the current font.
kvance
ah, ok. i get what you're intending to do now. in that case, it's a crap shoot. the app your user's pasting into can itself do whatever it wants to with pasted text.
pxl
+1  A: 

There is no system-wide or even application-wide notion of the "current font". The closest you can get is the typing attributes of a currently active NSTextView; if none is active, then there is nothing like a current font.

With that said, you could promise the RTF data to the pasteboard, and when it requests it, send the currently active application a copy AppleEvent command, wait for a response, pull any rich text off the pasteboard, and grab its font attributes. If no rich text is available, stick with your current font and size. I have no idea how well this heuristic works in practice, but I can't think of any better approach.

If I try and generate the plainest raw RTF data I can, with absolutely no font information, it still pastes in Helvetica 12!

The font defaults to Helvetica-12 when no font information is supplied because all text being drawn has to be in some font at some size. Helvetica-12 must have seemed as readable and sufficiently inoffensive to be chosen as the default. (Sure beats Comic Sans-72!)

Jeremy W. Sherman
Won't the copy operation usually come up empty without a selection? I'll play around with this idea though, I like it.
kvance