Форматирование вывода NSTask - PullRequest
1 голос
/ 26 января 2011

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

Полужирный текст с подчеркиванием

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

Вместо этого возвращается следующее:

Текст BBoolldd с _u_n_d_e_r_l_i_n_e

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

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

Ответы [ 3 ]

4 голосов
/ 26 января 2011

Какова ваша настоящая цель?Если вы хотите отобразить справочную страницу, один из вариантов - преобразовать ее в 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.

2 голосов
/ 26 января 2011

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

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

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

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

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

Последующие Up:

У меня есть хороший быстрый анализатор для того, чтобы взять вывод человеком и заменить вывод жирным шрифтом / подчеркиванием на форматирование жирным шрифтом / подчеркнутым в 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)];
    }
}];
1 голос
/ 27 января 2011

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

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

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

...