Skip to content
Newer
Older
100644 791 lines (589 sloc) 28.8 KB
056cb11 initial commit
Philip Dow authored
1 //
2 // NSAttributedString+JournlerrAdditions.m
3 // Journler
4 //
5 // Created by Philip Dow on 6/10/06.
6dc7e9e updated copyright notice in most project files
Philip Dow authored
6 // Copyright 2006 Sprouted, Philip Dow. All rights reserved.
056cb11 initial commit
Philip Dow authored
7 //
8
6616b42 @phildow Added BSD style license notification project wide
authored
9 /*
10 Redistribution and use in source and binary forms, with or without modification, are permitted
11 provided that the following conditions are met:
12
13 * Redistributions of source code must retain the above copyright notice, this list of conditions
14 and the following disclaimer.
15
16 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
17 and the following disclaimer in the documentation and/or other materials provided with the
18 distribution.
19
20 * Neither the name of the author nor the names of its contributors may be used to endorse or
21 promote products derived from this software without specific prior written permission.
22
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
24 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
29 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 // Basically, you can use the code in your free, commercial, private and public projects
34 // as long as you include the above notice and attribute the code to Philip Dow / Sprouted
35 // If you use this code in an app send me a note. I'd love to know how the code is used.
36
37 // Please also note that this copyright does not supersede any other copyrights applicable to
38 // open source code used herein. While explicit credit has been given in the Journler about box,
39 // it may be lacking in some instances in the source code. I will remedy this in future commits,
40 // and if you notice any please point them out.
41
056cb11 initial commit
Philip Dow authored
42 #import "NSAttributedString+JournlerAdditions.h"
43 #import "NSURL+JournlerAdditions.h"
44 #import "JournlerJournal.h"
45 #import "JournlerEntry.h"
46
47 @implementation NSAttributedString (JournlerAdditions)
48
49 - (NSAttributedString*) attributedStringWithoutTextAttachments
50 {
f1550f5 completed project wide int->NSInteger and related refactoring
Philip Dow authored
51 NSInteger i, length = [self length];
056cb11 initial commit
Philip Dow authored
52 NSMutableAttributedString *attributedString = [[self mutableCopyWithZone:[self zone]] autorelease];
53
54 for ( i = length - 1; i > 0; i-- )
55 {
56 id attachment = [attributedString attribute:NSAttachmentAttributeName atIndex:i effectiveRange:nil];
57 if ( attachment != nil )
58 [attributedString deleteCharactersInRange:NSMakeRange(i,1)];
59 }
60
61 return attributedString;
62 }
63
64 - (NSAttributedString*) attributedStringWithoutJournlerLinks {
65
66 NSMutableAttributedString *mutable_str = [self mutableCopyWithZone:[self zone]];
67
68 NSRange limitRange;
69 NSRange effectiveRange;
70 id attr_value;
71
72 limitRange = NSMakeRange(0, [mutable_str length]);
73
74 while (limitRange.length > 0) {
75
76 attr_value = [mutable_str attribute:NSLinkAttributeName atIndex:limitRange.location
77 longestEffectiveRange:&effectiveRange inRange:limitRange];
78
79 if ( attr_value != nil ) {
80
81 if ( [attr_value isKindOfClass:[NSURL class]] &&
82 ( [attr_value isJournlerEntry] || [attr_value isJournlerResource] ) )
83 [mutable_str removeAttribute:NSLinkAttributeName range:effectiveRange];
84 else if ( [attr_value isKindOfClass:[NSString class]] ) {
85
86 NSURL *aURL = [NSURL URLWithString:attr_value];
87 if ( aURL != nil && ( [aURL isJournlerEntry] || [aURL isJournlerResource] ) )
88 [mutable_str removeAttribute:NSLinkAttributeName range:effectiveRange];
89
90 }
91
92 }
93
94 limitRange = NSMakeRange(NSMaxRange(effectiveRange), NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
95 }
96
97 return [mutable_str autorelease];
98
99 }
100
101 - (NSString*) iPodLinkedNote:(JournlerJournal*)aJournal {
102
103 NSMutableAttributedString *mutable_str = [self mutableCopyWithZone:[self zone]];
104
105 NSRange limitRange;
106 NSRange effectiveRange;
107 id attr_value;
108
109 limitRange = NSMakeRange(0, [mutable_str length]);
110
111 while (limitRange.length > 0) {
112
113 attr_value = [mutable_str attribute:NSLinkAttributeName atIndex:limitRange.location
114 longestEffectiveRange:&effectiveRange inRange:limitRange];
115
116 if ( [attr_value isKindOfClass:[NSURL class]] && [attr_value isJournlerEntry] )
117 {
118 // journler entry link, make an ipod link
119 JournlerEntry *anEntry = [aJournal entryForTagString:[[attr_value absoluteString] lastPathComponent]];
120 if ( anEntry ) {
121
122 NSString *noteTitle = [NSString stringWithFormat:@"%@ %@ 1.txt", [anEntry pathSafeTitle], [anEntry tagID]];
123 NSString *currentText = [[mutable_str string] substringWithRange:effectiveRange];
124
125 NSString *linkedText = [NSString stringWithFormat:@"<a href=\"%@\">%@</a>",
126 noteTitle, currentText];
127
128 [mutable_str removeAttribute:NSLinkAttributeName range:effectiveRange];
129 [mutable_str replaceCharactersInRange:effectiveRange withString:linkedText];
130 }
131 }
132
133 limitRange = NSMakeRange(NSMaxRange(effectiveRange), NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
134 }
135
136 return [[mutable_str autorelease] string];
137
138 }
139
140 - (NSData*) firstImageData:(NSRange)aRange fileType:(NSBitmapImageFileType)type
141 {
142
143 //
144 // parse the string for an image, return it
145
146 NSData *returnData = nil;
147
148 NSRange limitRange;
149 NSRange effectiveRange;
150 id attr_value;
151
152 limitRange = aRange;
153 if ( limitRange.location + limitRange.length > [self length] ) limitRange.length = [self length] - limitRange.length;
154
155 while (limitRange.length > 0)
156 {
157 attr_value = [self attribute:NSAttachmentAttributeName atIndex:limitRange.location
158 longestEffectiveRange:&effectiveRange inRange:limitRange];
159
160 limitRange = NSMakeRange(NSMaxRange(effectiveRange), NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
161
162 if ( ![attr_value isKindOfClass:[NSTextAttachment class]] )
163 continue;
164
165 NSFileWrapper *wrapper = [attr_value fileWrapper];
166 if ( !wrapper )
167 continue;
168
169 NSImage *image = [[[NSImage alloc] initWithData:[wrapper regularFileContents]] autorelease];
170 if ( !image )
171 continue;
172
173 NSBitmapImageRep *bitmapRep = [[[NSBitmapImageRep alloc] initWithData:[image TIFFRepresentation]] autorelease];
174 if ( !bitmapRep )
175 continue;
176
177 NSData *imageData = [bitmapRep representationUsingType:type properties:nil];
178 if ( !imageData )
179 continue;
180
181 // if we made it this far, this is a valid image
182 returnData = imageData;
183 break;
184 }
185
186 return returnData;
187 }
188
189 #pragma mark -
190
191 /*
192 - (id) htmlRepresentation:(BOOL)systemConversion documentAttributes:(NSDictionary*)options {
193
194 //Returns NSData on sysemConversion, NSString if not.
195 //
196 //An attempt to handle some basic rich text to html parsing
197 // 1) bold
198 // 2) italic
199 // 3) underline
200 // 4) links
201 // 5) misc special characters such as greater than, less than, and NSAttachmentCharacter
202 //
203
204 BOOL sys_converted = NO;
205
206 id return_object;
207
208 if ( systemConversion ) {
209
210 NSMutableDictionary *docAttributes;
211 NSError *conversionError;
212 NSData *htmlData;
213
214 //
215 // prepare the standard items
216 NSArray *excludedItems = [NSArray arrayWithObjects:
217 @"APPLET", @"BASEFONT", @"CENTER", @"DIR", @"FONT", @"ISINDEX", @"MENU", @"S", @"STRIKE", @"U", nil];
218
219 docAttributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:
220 NSHTMLTextDocumentType, NSDocumentTypeDocumentAttribute,
f1550f5 completed project wide int->NSInteger and related refactoring
Philip Dow authored
221 [NSNumber numberWithInteger:2], NSPrefixSpacesDocumentAttribute,
056cb11 initial commit
Philip Dow authored
222 excludedItems, NSExcludedElementsDocumentAttribute, nil];
223
224 //
225 // add any additional items
226 if ( options != nil )
227 [docAttributes addEntriesFromDictionary:options];
228
229
230 htmlData = [self dataFromRange:NSMakeRange(0,[self length])
231 documentAttributes:docAttributes error:&conversionError];
232
233 if ( htmlData ) {
234 sys_converted = YES;
235 return_object = [htmlData retain];
236
237 }
238 else {
239
240 NSLog(@"htmlRepresentation:documentAttributes: conversion error %@", [conversionError description]);
241 sys_converted = NO;
242
243 }
244
245 }
246
247 if ( !sys_converted ) {
248
249 NSFontManager *fontManager = [[NSFontManager sharedFontManager] retain];
250 NSMutableString *html = [[NSMutableString alloc] init];
251
252 NSFont *lastFont = [[NSFont systemFontOfSize:12.0] retain];
253 NSFont *thisFont;
254
255 NSNumber *lastUnderline = nil;
256 NSNumber *thisUnderline = nil;
257
258 NSRange effectiveRange;
259
f1550f5 completed project wide int->NSInteger and related refactoring
Philip Dow authored
260 NSInteger i;
056cb11 initial commit
Philip Dow authored
261 for ( i = 0; i < [self length]; i++ ) {
262
263 //grab the attributes of our current character
264 //if the current character is a newline or return, we need to clear its traits
265 if ( [[self string] characterAtIndex:i] == NSNewlineCharacter ||
266 [[self string] characterAtIndex:i] == NSCarriageReturnCharacter ) {
267 thisFont = [NSFont systemFontOfSize:12.0]; // neutral font
268 thisUnderline = nil; // neutral underline
269 }
270 else {
271 thisFont = [self attribute:NSFontAttributeName atIndex:i effectiveRange:nil];
272 thisUnderline = [self attribute:NSUnderlineStyleAttributeName atIndex:i effectiveRange:nil];
273 }
274
275 //check for a link
276 if ( [self attribute:NSLinkAttributeName atIndex:i effectiveRange:&effectiveRange] ) {
277
278 //grab the link text
279 NSString *linkSub = [[self string] substringWithRange:effectiveRange];
280 NSString *linkString;
281
282 //grab the url from this guy and use it if it exists
283 id linkURL = [self attribute:NSLinkAttributeName atIndex:i effectiveRange:nil];
284
285 if ( linkURL && [linkURL isKindOfClass:[NSURL class]] ) {
286
287 if ( [linkURL isJournlerEntry] || [linkURL isJournlerResource] ) {
288
289 //
290 // watch out for attachments at the link as well
291
292 NSString *blue_text;
293 if ( [[self string] characterAtIndex:i] == NSAttachmentCharacter )
294 blue_text = @"<journler info='rtfd' note='insert image here'></journler>";
295 else
296 blue_text = (NSString*)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,(CFStringRef)linkSub,CFSTR(""),kCFStringEncodingUTF8);
297
298 linkString = [NSString stringWithFormat:@"<journler info='link' note='%@'>%@</journler>",
299 [linkURL absoluteString], blue_text];
300
301 }
302 else {
303
304 linkString = [NSString stringWithFormat:@"<a href='%@'>%@</a>", [linkURL absoluteString],
305 (NSString*)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,(CFStringRef)linkSub,CFSTR(""),kCFStringEncodingUTF8)];
306
307 }
308 }
309 else if ( linkURL && [linkURL isKindOfClass:[NSString class]] ) {
310 linkString = [NSString stringWithFormat:@"<a href='%@'>%@</a>", linkURL,
311 (NSString*)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,(CFStringRef)linkSub,CFSTR(""),kCFStringEncodingUTF8)];
312 }
313 else {
314 linkString = [NSString stringWithFormat:@"<a href='%@'>%@</a>", linkSub,
315 (NSString*)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,(CFStringRef)linkSub,CFSTR(""),kCFStringEncodingUTF8)];
316 }
317
318 //append it and change the distance
319 [html appendString:linkString];
320 i+= (effectiveRange.length - 1);
321
322 }
323 else {
324
325 //check for old attribtues in order reverse to new
326 if ( lastUnderline && !thisUnderline )
327 [html appendString:@"</u>"];
328
329 if ( [fontManager fontNamed:[lastFont fontName] hasTraits:NSItalicFontMask]
330 && ![fontManager fontNamed:[thisFont fontName] hasTraits:NSItalicFontMask] )
331 [html appendString:@"</em>"]; // italic
332
333 if ( [fontManager fontNamed:[lastFont fontName] hasTraits:NSBoldFontMask]
334 && ![fontManager fontNamed:[thisFont fontName] hasTraits:NSBoldFontMask] )
335 [html appendString:@"</strong>"]; // bold
336
337 //check for new attributes
338 if ( ![fontManager fontNamed:[lastFont fontName] hasTraits:NSBoldFontMask]
339 && [fontManager fontNamed:[thisFont fontName] hasTraits:NSBoldFontMask] )
340 [html appendString:@"<strong>"];
341
342 if ( ![fontManager fontNamed:[lastFont fontName] hasTraits:NSItalicFontMask]
343 && [fontManager fontNamed:[thisFont fontName] hasTraits:NSItalicFontMask] )
344 [html appendString:@"<em>"];
345
346 if ( !lastUnderline && thisUnderline )
347 [html appendString:@"<u>"];
348
349 //and append our character, weeding out greater than and less than signs
350 if ( [[self string] characterAtIndex:i] == '<' )
351 [html appendString:@"&lt;"];
352 else if ( [[self string] characterAtIndex:i] == '>' )
353 [html appendString:@"&gt;"];
354 else if ( [[self string] characterAtIndex:i] == NSCarriageReturnCharacter )
355 [html appendString:[NSString stringWithCharacters:(const unichar[]) {NSNewlineCharacter} length:1]];
356 else if ( [[self string] characterAtIndex:i] == NSAttachmentCharacter )
357 [html appendString:@"<journler info='rtfd' note='insert image here'></journler>"];
358 else
359 [html appendString:[[self string] substringWithRange:NSMakeRange(i,1)]];
360
361 //set our last font to this font
362 [lastFont release];
363 lastFont = [thisFont copyWithZone:[self zone]];
364
365 if ( lastUnderline ) [lastUnderline release];
366 lastUnderline = ( thisUnderline ? [thisUnderline copy] : nil );
367
368 }
369 }
370
371 //once we are through the processing, we must append any closing markup, again in reverse order for consistency
372 if ( lastUnderline ) [html appendString:@"</u>"];
373 if ( [fontManager fontNamed:[lastFont fontName] hasTraits:NSItalicFontMask] ) [html appendString:@"</em>"];
374 if ( [fontManager fontNamed:[lastFont fontName] hasTraits:NSBoldFontMask] ) [html appendString:@"</strong>"];
375
376 // clean up
377 [fontManager release];
378 [lastFont release];
379
380 return_object = html;
381
382 }
383
384 return [return_object autorelease];
385
386 }
387 */
388
389 - (NSString*) attributedStringAsHTML:(RichTextToHTMLOptions)options documentAttributes:(NSDictionary*)docAttrs avoidStyleAttributes:(NSString*)noList
390 {
391 if ( [self length] == 0 )
392 return [NSString string];
393
394 NSString *htmlString = nil;
395
396 if ( options & kUseSystemHTMLConversion )
397 {
398 NSMutableDictionary *docAttributes;
399 NSError *conversionError;
400 NSData *htmlData;
401
402 // prepare the standard items
403 NSArray *excludedItems = [NSArray arrayWithObjects:
404 @"APPLET", @"BASEFONT", @"CENTER", @"DIR", @"FONT", @"ISINDEX", @"MENU", @"S", @"STRIKE", @"U", nil];
405
406 // document atttributes
407 docAttributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:
408 NSHTMLTextDocumentType, NSDocumentTypeDocumentAttribute,
f1550f5 completed project wide int->NSInteger and related refactoring
Philip Dow authored
409 [NSNumber numberWithInteger:2], NSPrefixSpacesDocumentAttribute,
056cb11 initial commit
Philip Dow authored
410 excludedItems, NSExcludedElementsDocumentAttribute, nil];
411
412 [docAttributes addEntriesFromDictionary:docAttrs];
413
414 htmlData = [self dataFromRange:NSMakeRange(0,[self length]) documentAttributes:docAttributes error:&conversionError];
415 if ( htmlData == nil )
416 {
8a15dcf project wide pretty function find and replace
Philip Dow authored
417 NSLog(@"%s - unable to generate html data from rich text", __PRETTY_FUNCTION__ );
056cb11 initial commit
Philip Dow authored
418 htmlString = nil;
419 goto bail;
420 }
421 else
422 {
423 NSString *tentative = [[[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding] autorelease];
424 if ( options & kUseInlineStyleDefinitions )
425 htmlString = [self _htmlWithInlineStyleDefinitions:tentative bannedStyleAttributes:noList];
426 else
427 htmlString = tentative;
428 }
429
430 }
431 else
432 {
433 htmlString = [self _htmlUsingJournlerConverter];
434 }
435
436 bail:
437
438 if ( options & kConvertSmartQuotesToRegularQuotes )
439 {
440 static unichar kOpenSmartQuote = 0x201C; // 0x201C; //0x0093;
441 static unichar kCloseSmartQuote = 0x201D; // 0x201D; // 0x0094;
442 //static unichar kRegularQuote = 0x22;
443 NSString *openSmartQuote = [[[NSString alloc] initWithCharacters:(const unichar[]){kOpenSmartQuote} length:1] autorelease];
444 NSString *closeSmartQuote = [[[NSString alloc] initWithCharacters:(const unichar[]){kCloseSmartQuote} length:1] autorelease];
445
446 NSMutableString *workingString = [[htmlString mutableCopyWithZone:[self zone]] autorelease];
447 [workingString replaceOccurrencesOfString:openSmartQuote withString:@"\"" options:NSLiteralSearch range:NSMakeRange(0,[workingString length])];
448 [workingString replaceOccurrencesOfString:closeSmartQuote withString:@"\"" options:NSLiteralSearch range:NSMakeRange(0,[workingString length])];
449
450 htmlString = workingString;
451
452 //htmlString = [htmlString stringByReplacingOccurrencesOfCharacter:kOpenSmartQuote withCharacter:kRegularQuote];
453 //htmlString = [htmlString stringByReplacingOccurrencesOfCharacter:kCloseSmartQuote withCharacter:kRegularQuote];
454
455 }
456
457 return htmlString;
458
459 }
460
461 - (NSString*) _htmlUsingJournlerConverter
462 {
463 NSFontManager *fontManager = [[NSFontManager sharedFontManager] retain];
464 NSMutableString *html = [[NSMutableString alloc] init];
465
466 NSFont *lastFont = [[NSFont systemFontOfSize:12.0] retain];
467 NSFont *thisFont;
468
469 NSNumber *lastUnderline = nil;
470 NSNumber *thisUnderline = nil;
471
472 NSRange effectiveRange;
473
f1550f5 completed project wide int->NSInteger and related refactoring
Philip Dow authored
474 NSInteger i;
056cb11 initial commit
Philip Dow authored
475 for ( i = 0; i < [self length]; i++ ) {
476
477 //grab the attributes of our current character
478 //if the current character is a newline or return, we need to clear its traits
479 if ( [[self string] characterAtIndex:i] == NSNewlineCharacter ||
480 [[self string] characterAtIndex:i] == NSCarriageReturnCharacter )
481 {
482 thisFont = [NSFont systemFontOfSize:12.0]; // neutral font
483 thisUnderline = nil; // neutral underline
484
485 // add paragraph and br tags
486 if ( i + 1 < [self length] - 1 )
487 {
488 if ( i != 0 && ( [[self string] characterAtIndex:i-1] == NSNewlineCharacter || [[self string] characterAtIndex:i-1] == NSCarriageReturnCharacter ) )
489 {
490 // don't do anything if the previous character was a newline
491 }
492 else
493 {
494 // if the next character is also newline, this is a paragraph break, otherwise just a line break
495 if ( [[self string] characterAtIndex:i+1] == NSNewlineCharacter || [[self string] characterAtIndex:i+1] == NSCarriageReturnCharacter )
496 [html appendString:@"<p></p>"];
497 else
498 [html appendString:@"<br />"];
499 }
500 }
501 else
502 {
503 // if we're at the end encountering newline then just add a line break
504 [html appendString:@"<br />"];
505 }
506 }
507 else
508 {
509 thisFont = [self attribute:NSFontAttributeName atIndex:i effectiveRange:nil];
510 thisUnderline = [self attribute:NSUnderlineStyleAttributeName atIndex:i effectiveRange:nil];
511 }
512
513 //check for a link
514 if ( [self attribute:NSLinkAttributeName atIndex:i effectiveRange:&effectiveRange] ) {
515
516 //grab the link text
517 NSString *linkSub = [[self string] substringWithRange:effectiveRange];
518 NSString *linkString;
519
520 //grab the url from this guy and use it if it exists
521 id linkURL = [self attribute:NSLinkAttributeName atIndex:i effectiveRange:nil];
522
523 if ( linkURL && [linkURL isKindOfClass:[NSURL class]] ) {
524
525 if ( [linkURL isJournlerEntry] || [linkURL isJournlerResource] ) {
526
527 //
528 // watch out for attachments at the link as well
529
530 NSString *blue_text;
531 if ( [[self string] characterAtIndex:i] == NSAttachmentCharacter )
532 blue_text = @"<journler info=\"rtfd\" note=\"insert image here\"></journler>";
533 else
534 blue_text = (NSString*)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,(CFStringRef)linkSub,CFSTR(""),kCFStringEncodingUTF8);
535
536 linkString = [NSString stringWithFormat:@"<journler info=\"link\" note=\"%@\">%@</journler>",
537 [linkURL absoluteString], blue_text];
538
539 }
540 else {
541
542 linkString = [NSString stringWithFormat:@"<a href=\"%@\">%@</a>", [linkURL absoluteString],
543 (NSString*)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,(CFStringRef)linkSub,CFSTR(""),kCFStringEncodingUTF8)];
544
545 }
546 }
547 else if ( linkURL && [linkURL isKindOfClass:[NSString class]] ) {
548 linkString = [NSString stringWithFormat:@"<a href=\"%@\">%@</a>", linkURL,
549 (NSString*)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,(CFStringRef)linkSub,CFSTR(""),kCFStringEncodingUTF8)];
550 }
551 else {
552 linkString = [NSString stringWithFormat:@"<a href=\"%@\">%@</a>", linkSub,
553 (NSString*)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,(CFStringRef)linkSub,CFSTR(""),kCFStringEncodingUTF8)];
554 }
555
556 //append it and change the distance
557 [html appendString:linkString];
558 i+= (effectiveRange.length - 1);
559
560 }
561 else {
562
563 //check for old attribtues in order reverse to new
564 if ( lastUnderline && !thisUnderline )
565 [html appendString:@"</span>"];
566
567 if ( [fontManager fontNamed:[lastFont fontName] hasTraits:NSItalicFontMask]
568 && ![fontManager fontNamed:[thisFont fontName] hasTraits:NSItalicFontMask] )
569 [html appendString:@"</em>"]; /* italic */
570
571 if ( [fontManager fontNamed:[lastFont fontName] hasTraits:NSBoldFontMask]
572 && ![fontManager fontNamed:[thisFont fontName] hasTraits:NSBoldFontMask] )
573 [html appendString:@"</strong>"]; /* bold */
574
575 //check for new attributes
576 if ( ![fontManager fontNamed:[lastFont fontName] hasTraits:NSBoldFontMask]
577 && [fontManager fontNamed:[thisFont fontName] hasTraits:NSBoldFontMask] )
578 [html appendString:@"<strong>"];
579
580 if ( ![fontManager fontNamed:[lastFont fontName] hasTraits:NSItalicFontMask]
581 && [fontManager fontNamed:[thisFont fontName] hasTraits:NSItalicFontMask] )
582 [html appendString:@"<em>"];
583
584 if ( !lastUnderline && thisUnderline )
585 [html appendString:@"<span style=\"text-decoration: underline\">"];
586
587 //and append our character, weeding out greater than and less than signs
588 if ( [[self string] characterAtIndex:i] == '<' )
589 [html appendString:@"&lt;"];
590 else if ( [[self string] characterAtIndex:i] == '>' )
591 [html appendString:@"&gt;"];
592 else if ( [[self string] characterAtIndex:i] == NSCarriageReturnCharacter )
593 [html appendString:[NSString stringWithCharacters:(const unichar[]) {NSNewlineCharacter} length:1]];
594 else if ( [[self string] characterAtIndex:i] == NSAttachmentCharacter )
595 [html appendString:@"<journler info=\"rtfd\" note=\"insert image here\"></journler>"];
596 else
597 [html appendString:[[self string] substringWithRange:NSMakeRange(i,1)]];
598
599 //set our last font to this font
600 [lastFont release];
601 lastFont = [thisFont copyWithZone:[self zone]];
602
603 if ( lastUnderline ) [lastUnderline release];
604 lastUnderline = ( thisUnderline ? [thisUnderline copy] : nil );
605
606 }
607 }
608
609 //once we are through the processing, we must append any closing markup, again in reverse order for consistency
610 if ( lastUnderline ) [html appendString:@"</u>"];
611 if ( [fontManager fontNamed:[lastFont fontName] hasTraits:NSItalicFontMask] ) [html appendString:@"</em>"];
612 if ( [fontManager fontNamed:[lastFont fontName] hasTraits:NSBoldFontMask] ) [html appendString:@"</strong>"];
613
614 // clean up
615 [fontManager release];
616 [lastFont release];
617
618 return html;
619 }
620
621 - (NSString*) _htmlWithInlineStyleDefinitions:(NSString*)html bannedStyleAttributes:(NSString*)noList
622 {
623 // the bannedStyleAttributes contains a comma separated list of style attributes that should be removed from the conversion
624 // substyles are also removes, so specifying "outline" will remove outline-color, outline-style and outline-width
625
626 if ( html == nil || [html length] == 0 )
627 return [NSString string];
628
f1550f5 completed project wide int->NSInteger and related refactoring
Philip Dow authored
629 NSInteger i;
056cb11 initial commit
Philip Dow authored
630 NSMutableArray *bannedAttributesList = nil;
631
632 if ( noList != nil && [noList length] != 0 )
633 {
634 NSArray *separatedList = [noList componentsSeparatedByString:@","];
635 bannedAttributesList = [NSMutableArray arrayWithCapacity:[separatedList count]];
636
637 for ( i = 0; i < [separatedList count]; i++ )
638 {
639 NSString *anItem = [separatedList objectAtIndex:i];
640 [bannedAttributesList addObject:[anItem stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
641 }
642
643 }
644
645 NSScanner *scanner = [NSScanner scannerWithString:html];
646 NSMutableDictionary *stylesDictionary = [NSMutableDictionary dictionary];
647
648 NSString *bodyOnly = nil;
649 NSMutableString *htmlInline = nil;
650
651 NSString *styleDefinition = nil;
652 NSArray *styleDefinitions = nil;
653
654 static NSString *styleBegin = @"<style type=\"text/css\">";
655 static NSString *styleEnd = @"</style>";
656
657 static NSString *bodyBegin = @"<body>";
658 static NSString *bodyEnd = @"</body>";
659
660 static NSString *zeroMargin = @"margin: 0.0px 0.0px 0.0px 0.0px; ";
661 static NSString *emptyStyle = @" style=\"\"";
662 static NSString *emptyParagraph = @"<p><br /></p>";
663
664 static NSString *boldOpen = @"<b>";
665 static NSString *boldClose = @"</b>";
666 static NSString *strongOpen = @"<strong>";
667 static NSString *strongClose = @"</strong>";
668
669 static NSString *italicOpen = @"<i>";
670 static NSString *italicClose = @"</i>";
671 static NSString *emphasisOpen = @"<em>";
672 static NSString *emphasisClose = @"</em>";
673
674 [scanner scanUpToString:styleBegin intoString:nil];
675 [scanner scanString:styleBegin intoString:nil];
676 [scanner scanUpToString:styleEnd intoString:&styleDefinition];
677
678 [scanner scanUpToString:bodyBegin intoString:nil];
679 [scanner scanString:bodyBegin intoString:nil];
680 [scanner scanUpToString:bodyEnd intoString:&bodyOnly];
681
682 htmlInline = [[bodyOnly mutableCopyWithZone:[self zone]] autorelease];
683
684 styleDefinitions = [styleDefinition componentsSeparatedByString:@"\n"];
685
686 //NSLog([styleDefinitions description]);
687
aab5f9d completed project-wide NSEnumerator / foreach in factoring. Some stra…
Philip Dow authored
688 for ( NSString *aStyleDefinition in styleDefinitions )
056cb11 initial commit
Philip Dow authored
689 {
690 aStyleDefinition = [aStyleDefinition stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
691 NSArray *styleComponents = [aStyleDefinition componentsSeparatedByString:@" {"];
692 if ( [styleComponents count] != 2 )
693 continue;
694
695 NSString *aStyleKey = [styleComponents objectAtIndex:0];
696 NSString *aStyleDeclaration = [styleComponents objectAtIndex:1];
697
698 if ( [aStyleDeclaration characterAtIndex:[aStyleDeclaration length]-1] == '}' )
699 aStyleDeclaration = [aStyleDeclaration substringToIndex:[aStyleDeclaration length]-1];
700
701 NSArray *keyComponents = [aStyleKey componentsSeparatedByString:@"."];
702 if ( [keyComponents count] != 2 )
703 continue;
704
705 // filter out the prohibited styles from the style declaration if requested
706 if ( bannedAttributesList != nil && [bannedAttributesList count] != 0 )
707 {
f1550f5 completed project wide int->NSInteger and related refactoring
Philip Dow authored
708 NSInteger i;
056cb11 initial commit
Philip Dow authored
709 aStyleDeclaration = [[aStyleDeclaration mutableCopyWithZone:[self zone]] autorelease];
710 NSCharacterSet *attributeEndSet = [NSCharacterSet characterSetWithCharactersInString:@";\""];
711
712 for ( i = 0; i < [bannedAttributesList count]; i++ )
713 {
f1550f5 completed project wide int->NSInteger and related refactoring
Philip Dow authored
714 NSInteger startIndex, endIndex;
056cb11 initial commit
Philip Dow authored
715 NSString *bannedAttribute = [bannedAttributesList objectAtIndex:i];
716 NSScanner *bannedScanner;
717
718 restart:
719 bannedScanner = [NSScanner scannerWithString:aStyleDeclaration];
720
721 // first the variations
722 while ( YES )
723 {
724 // find the banned string and note the location
725 if ( [aStyleDeclaration rangeOfString:bannedAttribute options:NSCaseInsensitiveSearch range:NSMakeRange(0,[aStyleDeclaration length])].location == 0 )
726 startIndex = 0;
727 else
728 {
729 if ( ![bannedScanner scanUpToString:bannedAttribute intoString:nil] )
730 break;
731
732 startIndex = [bannedScanner scanLocation];
733 }
734
735 // find the end of the attribute and note the location
736 if ( ![bannedScanner scanUpToCharactersFromSet:attributeEndSet intoString:nil] )
737 break;
738
739 endIndex = [bannedScanner scanLocation];
740
741 // modify the end index to get rid of extra spaces and the ending colon
742 if ( endIndex + 1 < [aStyleDeclaration length] )
743 endIndex+=2;
744
745 // simple error checking
746 if ( endIndex < startIndex )
747 break;
748
749 NSRange bannedRange = NSMakeRange(startIndex, endIndex - startIndex);
750 [(NSMutableString*)aStyleDeclaration deleteCharactersInRange:bannedRange];
751
752 if ( [bannedScanner isAtEnd] )
753 break;
754 else
755 goto restart;
756 }
757 }
758 }
759
760 //NSLog(aStyleDeclaration);
761
762 //NSString *keyObject = [keyComponents objectAtIndex:0];
763 NSString *keyClass = [keyComponents objectAtIndex:1];
764
765 NSString *classProperty = [NSString stringWithFormat:@"class=\"%@\"", keyClass];
766 NSString *styleProperty = [NSString stringWithFormat:@"style=\"%@\"", aStyleDeclaration];
767
768 [stylesDictionary setObject:aStyleDeclaration forKey:aStyleKey];
769
770 [htmlInline replaceOccurrencesOfString:classProperty withString:styleProperty options:NSLiteralSearch range:NSMakeRange(0,[htmlInline length])];
771 }
772
773 //NSLog([stylesDictionary description]);
774
775 // do some final clean up
776 [htmlInline replaceOccurrencesOfString:zeroMargin withString:@"" options:NSLiteralSearch range:NSMakeRange(0,[htmlInline length])];
777 [htmlInline replaceOccurrencesOfString:emptyStyle withString:@"" options:NSLiteralSearch range:NSMakeRange(0,[htmlInline length])];
778 [htmlInline replaceOccurrencesOfString:emptyParagraph withString:@"" options:NSLiteralSearch range:NSMakeRange(0,[htmlInline length])];
779
780 [htmlInline replaceOccurrencesOfString:boldOpen withString:strongOpen options:NSLiteralSearch range:NSMakeRange(0,[htmlInline length])];
781 [htmlInline replaceOccurrencesOfString:boldClose withString:strongClose options:NSLiteralSearch range:NSMakeRange(0,[htmlInline length])];
782
783 [htmlInline replaceOccurrencesOfString:italicOpen withString:emphasisOpen options:NSLiteralSearch range:NSMakeRange(0,[htmlInline length])];
784 [htmlInline replaceOccurrencesOfString:italicClose withString:emphasisClose options:NSLiteralSearch range:NSMakeRange(0,[htmlInline length])];
785
786 return htmlInline;
787 }
788
789
790 @end
Something went wrong with that request. Please try again.