Форматирование вывода NSTask

Я использую NSTask для получения вывода из /usr/bin/man. Я получаю вывод, но без форматирования (жирный, подчеркнутый). Что-то, что должно выглядеть так:

Жирный текст с подчеркиванием

(обратите внимание, что текст, выделенный курсивом, на самом деле подчеркнут, здесь просто нет форматирования)

Вместо этого возвращается вот так:

Текст BBoolldd с _u_n_d_e_r_l_i_n_e

У меня есть минимальный тестовый проект по адресу http://cl.ly/052u2z2i2R280T3r1K3c, который вы можете скачать и запустить; обратите внимание, что окно ничего не делает; вывод регистрируется в консоли.

Я предполагаю, что мне нужно как-то интерпретировать объект NSData вручную, но я понятия не имею, с чего начать. В идеале я хотел бы перевести его в NSAttributedString, но в первую очередь нужно устранить дубликаты и символы подчеркивания. Есть предположения?


person Justin Mrkva    schedule 26.01.2011    source источник


Ответы (3)


Какова ваша настоящая цель? Если вы хотите отобразить справочную страницу, один из вариантов — преобразовать ее в HTML и отобразить в веб-представлении.

Анализ вывода man может быть сложным, поскольку он обрабатывается groff с использованием терминального процессора по умолчанию. Это означает, что выходные данные адаптированы для отображения на оконечных устройствах.

Одним из альтернативных решений является определение фактического местоположения исходного файла страницы руководства, например.

$ man -w bash
/usr/share/man/man1/bash.1.gz

и вручную вызвать на нем groff с -a (приближение ASCII) и -c (отключить вывод цвета), например.

$ gunzip -c /usr/share/man/man1/bash.1.gz | groff -c -a -Tascii -man

Это приведет к созданию файла ASCII без большей части форматирования. Чтобы сгенерировать вывод HTML,

$ gunzip -c /usr/share/man/man1/bash.1.gz | groff -Thtml -man

Вы также можете указать эти параметры в пользовательском файле конфигурации для man, например. parseman.conf и скажите man использовать этот файл конфигурации с параметром -C вместо вызова man -w, gunzip и groff. Файл конфигурации по умолчанию — /private/etc/man.conf.

Кроме того, вы, вероятно, можете настроить вывод процессора терминального устройства, передав соответствующие параметры в grotty.

person Community    schedule 26.01.2011
comment
Спасибо, это отличный ответ. В итоге я собрал работающий синтаксический анализатор, который на самом деле быстрее, чем генерация HTML-файла, поэтому я, вероятно, останусь при своем решении. Я также опубликую основы исходного кода синтаксического анализатора вместе с моим самостоятельным ответом. - person Justin Mrkva; 27.01.2011

Хорошо, вот начало моего решения, хотя мне были бы интересны любые дополнительные (более простые?) способы сделать это.

Выходные данные, возвращаемые Терминалом, представляют собой кодировку UTF-8, но NSUTF8StringEncoding неправильно интерпретирует строку. Причина в том, как форматируется вывод NSTask.

Буква N — это 0x4e в UTF-8. Но соответствующий этому NSData — 0x4e 0x08 0x4e. 0x08 соответствует Backspace. Таким образом, для жирного шрифта Терминал печатает букву-забой-букву.

Для курсива c это 0x63 в UTF-8. NSData содержит 0x5f 0x08 0x63, где 0x5f соответствует символу подчеркивания. Итак, для курсива Терминал печатает символ подчеркивания-забоя.

На данный момент я действительно не вижу никакого способа обойти это, кроме как просто сканировать необработанные NSData для этих последовательностей. Я, вероятно, опубликую здесь исходный код моего синтаксического анализатора, как только закончу его, если только у кого-нибудь нет существующего кода. Как гласит распространенная фраза программирования, никогда не пишите сами то, что можете скопировать. :)

Следовать за:

У меня есть хороший, быстрый синтаксический анализатор для получения вывода man и замены полужирного/подчеркнутого вывода полужирным/подчеркнутым форматированием в NSMutableAttributedString. Вот код, если кому-то еще нужно решить ту же проблему:

NSMutableIndexSet *boldChars = [[NSMutableIndexSet alloc] init];
NSMutableIndexSet *underlineChars = [[NSMutableIndexSet alloc] init];

char* bBytes = malloc(1);
bBytes[0] = (char)0x08;
NSData *bData = [NSData dataWithBytes:bBytes length:1];
free(bBytes); bBytes = nil;
NSRange testRange = NSMakeRange(1, [inputData length] - 1);
NSRange bRange = NSMakeRange(0, 0);

do {
    bRange = [inputData rangeOfData:bData options:(NSDataSearchOptions)NULL range:testRange];
    if (bRange.location == NSNotFound || bRange.location > [inputData length] - 2) break;
    const char * buff = [inputData bytes];

    if (buff[bRange.location - 1] == 0x5f) {

        // it's an underline
        //NSLog(@"Undr %c\n", buff[bRange.location + 1]);
        [inputData replaceBytesInRange:NSMakeRange(bRange.location - 1, 2) withBytes:NULL length:0];
        [underlineChars addIndex:bRange.location - 1];
        testRange = NSMakeRange(bRange.location, [inputData length] - (bRange.location));

    } else if (buff[bRange.location - 1] == buff[bRange.location + 1]) {

        // It's a bold
        //NSLog(@"Bold %c\n", buff[bRange.location + 1]);
        [inputData replaceBytesInRange:NSMakeRange(bRange.location - 1, 2) withBytes:NULL length:0];
        [boldChars addIndex:bRange.location - 1];
        testRange = NSMakeRange(bRange.location, [inputData length] - (bRange.location));

    } else {

        testRange.location = bRange.location + 1;
        testRange.length = [inputData length] - testRange.location;
    }
} while (testRange.location <= [inputData length] - 3);

NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:[[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding]];

NSFont *font = [NSFont fontWithDescriptor:[NSFontDescriptor fontDescriptorWithName:@"Menlo" size:12] size:12];
NSFont *boldFont = [[NSFontManager sharedFontManager] convertFont:font toHaveTrait:NSBoldFontMask];

[str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [str length])];

__block NSUInteger begin = [underlineChars firstIndex];
__block NSUInteger end = begin;
[underlineChars enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
    if (idx - end < 2) {
        // it's the next item to the previous one
        end = idx;
    } else {
        // it's a split, so drop in the accumulated range and reset
        [str addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:NSMakeRange(begin, (end-begin)+1)];
        begin = idx;
        end = begin;
    }
    if (idx == [underlineChars lastIndex]) {
        [str addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:NSMakeRange(begin, (end-begin)+1)];
    }
}];

begin = [boldChars firstIndex];
end = begin;
[boldChars enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
    if (idx - end < 2) {
        // it's the next item to the previous one
        end = idx;
    } else {
        // it's a split, so drop in the accumulated range and reset
        [str addAttribute:NSFontAttributeName value:boldFont range:NSMakeRange(begin, (end-begin)+1)];
        begin = idx;
        end = begin;
    }
    if (idx == [underlineChars lastIndex]) {
        [str addAttribute:NSFontAttributeName value:boldFont range:NSMakeRange(begin, (end-begin)+1)];
    }
}];
person Justin Mrkva    schedule 26.01.2011

Другим методом может быть преобразование справочной страницы в исходный код PostScript, запуск его через конвертер PostScript в PDF и помещение в PDFView.

Реализация будет похожа на ответ Бавариуса, только с другими аргументами для groff (-Tps вместо -Thtml).

Это было бы самым медленным решением, но, вероятно, лучшим для печати.

person Peter Hosey    schedule 27.01.2011