5

我有一些输出是一个非常简单的 RTF 文件。当我生成此文档时,用户可以通过电子邮件发送它。这一切都很好。文档看起来不错。获得 NSAttributedString 后,我创建一个 NSData 块,并将其写入文件,如下所示:

NSData* rtfData = [attrString dataFromRange:NSMakeRange(0, [attrString length]) documentAttributes:@{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType} error:&error];

该文件可以通过电子邮件发送。当我检查电子邮件时,一切都很好。

现在,我的任务是在文档顶部添加一个 UIImage。太好了,所以我正在创建一个这样的属性字符串:

NSTextAttachment *attachment = [[NSTextAttachment alloc] init];

UIImage* image = [UIImage imageNamed:@"logo"];
attachment.image = image;
attachment.bounds = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);

NSMutableAttributedString *imageAttrString = [[NSAttributedString attributedStringWithAttachment:attachment] mutableCopy];

// sets the paragraph styling of the text attachment

NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init] ;

[paragraphStyle setAlignment:NSTextAlignmentCenter];            // centers image horizontally

[paragraphStyle setParagraphSpacing:10.0f];   // adds some padding between the image and the following section

[imageAttrString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [imageAttrString length])];
[imageAttrString appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n"]];

此时,在 Xcode 中,我可以对 imageAttrString 进行 QuickLook,它绘制得很好。

构建此字符串后,我将执行以下操作:

[attrString appendAttributedString:imageAttrString];

然后添加我最初生成的所有其他属性文本。

当我现在查看文件时,没有图像。QuickLook 在调试器中看起来不错,但最终输出中没有图像。

提前感谢您对此的任何帮助。

4

2 回答 2

6

虽然,RTF 确实支持 Windows 上的嵌入式图像,但显然它不支持 OS X。RTF 是由 Microsoft 开发的,他们在 1.5 版(http://en.wikipedia.org/wiki/Rich_Text_Format#Version_changes)中添加了嵌入式图像。我认为 Apple 采用了早期版本的格式,他们对文档中图像的解决方案是 RTFD。以下是 Apple 文档中关于 RTF 的说明:

富文本格式 (RTF) 是 Microsoft Corporation 设计的一种文本格式化语言。您可以使用带有散布的 RTF 命令、组和转义序列的纯文本来表示字符、段落和文档格式属性。RTF 被广泛用作文档交换格式,用于跨应用程序和计算平台传输文档及其格式信息。Apple 已经用本章描述的自定义命令扩展了 RTF。

所以没有提到图像。最后要证明 RTF 不支持 mac 上的图像,请下载此 RTF 文档- 它会在 Windows 写字板中显示照片,而不会在 OS X TextEdit 中显示。

因此,正如 Larme 所提到的 - 您应该在添加附件时选择 RTFD 文件类型。来自维基百科:

富文本格式目录,也称为 RTFD(由于其扩展名为 .rtfd)或带附件的富文本格式

虽然您将能够获得包含文本和图像的 NSData 对象(根据其大小判断),dataFromRange:documentAttributes:@{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType} error:]但您可能无法保存它以便成功打开它。至少——我做不到。

这可能是因为实际上 RTFD 不是一种文件格式——它是一种捆绑格式。要检查它,您可以在 Mac 上使用 TextEdit 创建一个新文档,向其中添加图像和文本并将其保存为文件。然后右键单击该文件并选择显示包内容,您会注意到该目录包含您的图像和 RTF 格式的文本。

但是,您将能够使用以下代码成功保存此文档:

NSFileWrapper *fileWrapper = [imageAttrString fileWrapperFromRange:NSMakeRange(0, [imageAttrString length]) documentAttributes:@{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType} error:&error];
[fileWrapper writeToURL:yourFileURL options:NSFileWrapperWritingAtomic originalContentsURL:nil error:&error];

因为显然 NSFileWrapper 知道如何处理 RTFD 文档,而 NSData 不知道它包含什么。

然而,主要问题仍然存在——如何通过电子邮件发送?因为 RTFD 文档是一个目录而不是文件,我想说它不太适合通过电子邮件发送,但是您可以将其压缩并使用扩展名.rtfd.zip发送。这里的扩展至关重要,因为它会告诉邮件应用程序在用户点击附件时如何显示附件的内容。实际上它也适用于 Gmail 和 iOS 上的其他电子邮件应用程序,因为它是知道如何显示 .rtfd.zip 的 UIWebView。这是关于它的技术说明:https ://developer.apple.com/library/ios/qa/qa1630/_index.html#//apple_ref/doc/uid/DTS40008749

所以底线是 - 它可以完成,但 RTFD 文档将是电子邮件的附件,而不是电子邮件内容本身。如果您想将其作为电子邮件内容,您可能应该考虑将图像嵌入 HTML 并以 HTML 格式发送邮件。

于 2014-05-12T11:25:07.920 回答
6

正如 Andris 所提到的,Apple RTF 实现不支持嵌入式图像。

RTFD 并不是真正的替代品,因为只有少数 OS X 应用程序可以打开 RTFD 文件。例如 MS Office 不能。

在某些情况下,创建带有嵌入图像的 HTML 文件可能会有所帮助,但是 - 例如 - 大多数电子邮件客户端不支持带有嵌入图像的 HTML(Apple Mail 支持,但 Outlook 不支持)。

但幸运的是,有一个解决方案可以创建带有嵌入图像的真实 RTF 文件!

由于 RTF 格式当然支持嵌入图像(只有 Apple 的实现不支持),所以 NSAttributedStrings (NSTextAttachments) 中的图像可以(手动)编码到 RTF 流中。

以下类别完成了所有需要的工作:

/**
 NSAttributedString (MMRTFWithImages)

 */
@interface NSAttributedString (MMRTFWithImages)

- (NSString *)encodeRTFWithImages;

@end

/**
 NSAttributedString (MMRTFWithImages)

 */
@implementation NSAttributedString (MMRTFWithImages)

/*
 encodeRTFWithImages

 */
- (NSString *)encodeRTFWithImages {

    NSMutableAttributedString*  stringToEncode = [[NSMutableAttributedString alloc] initWithAttributedString:self];
    NSRange                     strRange = NSMakeRange(0, stringToEncode.length);

    //
    // Prepare the attributed string by removing the text attachments (images) and replacing them by
    // references to the images dictionary
    NSMutableDictionary*        attachmentDictionary = [NSMutableDictionary dictionary];
    while (strRange.length) {
        // Get the next text attachment
        NSRange effectiveRange;
        NSTextAttachment* textAttachment = [stringToEncode attribute:NSAttachmentAttributeName
                                                             atIndex:strRange.location
                                                      effectiveRange:&effectiveRange];

        strRange = NSMakeRange(NSMaxRange(effectiveRange), NSMaxRange(strRange) - NSMaxRange(effectiveRange));

        if (textAttachment) {
            // Text attachment found -> store image to image dictionary and remove the attachment
            NSFileWrapper*  fileWrapper = [textAttachment fileWrapper];

            UIImage*    image = [[UIImage alloc] initWithData:[fileWrapper regularFileContents]];
            // Kepp image size
            UIImage*    scaledImage = [self imageFromImage:image
                                               withSize:textAttachment.bounds.size];
            NSString*   imageKey = [NSString stringWithFormat:@"_MM_Encoded_Image#%zi_", [scaledImage hash]];
            [attachmentDictionary setObject:scaledImage
                                     forKey:imageKey];

            [stringToEncode removeAttribute:NSAttachmentAttributeName
                                      range:effectiveRange];
            [stringToEncode replaceCharactersInRange:effectiveRange
                                          withString:imageKey];
            strRange.length += [imageKey length] - 1;
        } // if
    } // while

    //
    // Create the RTF stream; without images but including our references
    NSData*             rtfData = [stringToEncode dataFromRange:NSMakeRange(0, stringToEncode.length)
                                    documentAttributes:@{
                                                         NSDocumentTypeDocumentAttribute:   NSRTFTextDocumentType
                                                         }
                                                 error:NULL];
    NSMutableString*    rtfString = [[NSMutableString alloc] initWithData:rtfData
                                                              encoding:NSASCIIStringEncoding];

    //
    // Replace the image references with hex encoded image data
    for (id key in attachmentDictionary) {
        NSRange     keyRange = [rtfString rangeOfString:(NSString*)key];
        if (NSNotFound != keyRange.location) {
            // Reference found -> replace with hex coded image data
            UIImage*    image = [attachmentDictionary objectForKey:key];
            NSData*     pngData = UIImagePNGRepresentation(image);

            NSString*   hexCodedString = [self hexadecimalRepresentation:pngData];
            NSString*   encodedImage = [NSString stringWithFormat:@"{\\*\\shppict {\\pict \\pngblip %@}}", hexCodedString];

            [rtfString replaceCharactersInRange:keyRange withString:encodedImage];
        }
    }
    return rtfString;
}

/*
 imageFromImage:withSize:

 Scales the input image to pSize
 */
- (UIImage *)imageFromImage:(UIImage *)pImage
                   withSize:(CGSize)pSize {

    UIGraphicsBeginImageContextWithOptions(pSize, NO, 0.0);
    [pImage drawInRect:CGRectMake(0, 0, pSize.width, pSize.height)];

    UIImage*    resultImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return resultImage;
}

/*
 hexadecimalRepresentation:

 Returns a hex codes string for all bytes in a NSData object
 */
- (NSString *) hexadecimalRepresentation:(NSData *)pData {

    static const char*  hexDigits = "0123456789ABCDEF";

    NSString*   result = nil;

    size_t      length = pData.length;
    if (length) {

        NSMutableData*  tempData = [NSMutableData dataWithLength:(length << 1)];    // double length
        if (tempData) {
            const unsigned char*    src = [pData bytes];
            unsigned char*          dst = [tempData mutableBytes];

            if ((src) &&
                (dst)) {
                // encode nibbles
                while (length--) {
                    *dst++ = hexDigits[(*src >> 4) & 0x0F];
                    *dst++ = hexDigits[(*src++ & 0x0F)];
                } // while

                result = [[NSString alloc] initWithData:tempData
                                               encoding:NSASCIIStringEncoding];
            } // if
        } // if
    } // if
    return result;
}

@end

基本思想取自这篇文章

于 2015-03-21T09:26:35.410 回答