Nano Hash - криптовалюты, майнинг, программирование

проблема с сохранением NSAttributedString с изображением в файл RTF

У меня есть вывод, который представляет собой очень простой файл 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 я могу выполнить QuickLook для imageAttrString, и он отлично отрисовывается.

Как только эта строка построена, я делаю следующее:

[attrString appendAttributedString:imageAttrString];

А затем добавляю весь остальной текст с атрибутами, который я изначально сгенерировал.

Когда я смотрю на файл сейчас, изображения нет. QuickLook хорошо выглядит в отладчике, но в окончательном выводе нет изображения.

Заранее спасибо за любую помощь в этом.


  • Поскольку это был интересный вопрос, я попробовал несколько вещей. Я столкнулся с той же проблемой. Но, когда я пытаюсь играть с .rtfd (вместо .rtf) и открывать их с помощью TextWrangler или TextEdit, я вижу, что вложение включено больше, чем в файле rtf (где ничего не было): {{\NeXTGraphic Attachment.png \width400 \height400 }¨}, и куча данных в конце, которые в шестнадцатеричном дампе я увидел по маркерам: 89 50 4E 47 0D 0A 1A 0A. Это, возможно, что-то, чтобы изучить. Я также видел, что вы пробовали форумы Apple Dev, возможно, ошибка, о которой нужно сообщить. 10.05.2014

Ответы:


1

Хотя RTF поддерживает встроенные изображения в Windows, по-видимому, не в OS X. RTF был разработан Microsoft, и они добавили встроенные изображения в версию 1.5 (http://en.wikipedia.org/wiki/Rich_Text_Format#Version_changes). Я думаю, что Apple использовала более раннюю версию формата, и их решение для изображений в документах было RTFD. Вот что говорится в документации Apple о RTF:

Rich Text Format (RTF) — это язык форматирования текста, разработанный корпорацией Microsoft. Вы можете представлять символы, абзацы и атрибуты формата документа, используя обычный текст с вкраплениями RTF-команд, групп и управляющих последовательностей. RTF широко используется в качестве формата обмена документами для передачи документов с их информацией о форматировании между приложениями и вычислительными платформами. Apple расширила RTF пользовательскими командами, которые описаны в этой главе.

Поэтому изображения не упоминаются. Наконец, чтобы доказать, что RTF не поддерживает изображения на Mac, загрузите этот RTF-документ — он покажет фото в Windows WordPad и не будет показывать его в OS X TextEdit.

Итак, как упомянул Ларме, при добавлении вложений вы должны выбрать тип файла RTFD. Из Википедии:

Каталог расширенного текстового формата, также известный как RTFD (из-за его расширения .rtfd) или расширенный текстовый формат с вложениями.

Хотя вы сможете получить объект NSData, который содержит и текст, и изображение (судя по его размеру), через dataFromRange:documentAttributes:@{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType} error:] вы, вероятно, не сможете сохранить его, чтобы его можно было успешно открыть. По крайней мере - я не смог этого сделать.

Это, вероятно, потому, что на самом деле RTFD - это не формат файла, а формат бандла. Чтобы проверить это, вы можете использовать TextEdit на своем Mac, чтобы создать новый документ, добавить в него изображение и текст и сохранить его как файл. Затем щелкните правой кнопкой мыши этот файл и выберите «Показать содержимое пакета», и вы заметите, что каталог содержит как ваше изображение, так и текст в формате 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, потому что именно UIWebView знает, как отображать .rtfd.zip. Вот техническое примечание по этому поводу: https://developer.apple.com/library/ios/qa/qa1630/_index.html#//apple_ref/doc/uid/DTS40008749

Итак, суть в том, что это можно сделать, но документ RTFD будет вложением в электронное письмо, а не самим содержимым электронной почты. Если вы хотите, чтобы это было в качестве содержимого электронной почты, вам, вероятно, следует рассмотреть возможность встраивания изображения в HTML и отправки почты в формате HTML.

12.05.2014
  • Кстати, изображения можно вставлять в html-файлы, поэтому, если вам нужно отправить один файл с текстом + изображениями, рассмотрите html вместо RTF. 14.05.2014
  • Привет Андрис. Я думал сделать RTFD, но спецификация RTF говорит, что он может содержать изображения, как описано здесь: ссылка. И NSTextAttachment имеет свойство, которое принимает UIImage. Итак, имея это в виду, я должен сделать это. Если бы была какая-то документация, говорящая мне, что это полностью не поддерживается, я бы с удовольствием ее прочитал. Но похоже, что это то, для чего был создан этот API. Мне не понятно, что это не работает. 14.05.2014
  • Я обновил ответ, включив в него информацию о том, что RTF не поддерживает встроенные изображения на Mac. Также тот факт, что NSTextAttachment имеет свойство, которое принимает UIImage, не означает, что результирующий формат будет RTF. Как я уже упоминал в своем ответе, вы можете использовать свой код и сохранить его в RTFD. Таким образом, вы будете использовать NSTextAttachment для добавления изображения в документ RTFD. 14.05.2014

  • 2

    Как упомянул Андрис, реализация 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
    

    Основная идея взята из этой статьи.

    21.03.2015
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..