Форматиране на изхода на 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)


Каква е действителната ви цел? Ако искате да покажете man страница, една от опциите е да я конвертирате в HTML и да я изобразите с уеб изглед.

Разборът на изхода на man може да бъде труден, защото се обработва от groff с помощта на терминален процесор по подразбиране. Това означава, че изходът е пригоден да се показва на терминални устройства.

Едно алтернативно решение е да се определи действителното местоположение на изходния файл на man страницата, напр.

$ 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. Така че за удебелена буква, терминалът отпечатва буква-backspace-буква.

За курсив c, това е 0x63 в UTF-8. NSData съдържа 0x5f 0x08 0x63, като 0x5f съответства на долна черта. Така че за курсив, терминалът отпечатва долна черта-backspace-буква.

Наистина не виждам друг начин да заобиколя това в този момент, освен просто да сканирам необработените 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.

Изпълнението би било подобно на отговора на Bavarious, само с различни аргументи за groff (-Tps вместо -Thtml).

Това би било най-бавното решение, но вероятно и най-доброто за печат.

person Peter Hosey    schedule 27.01.2011