1

我正在使用 NSTask 从 /usr/bin/man 获取输出。我得到了输出,但没有格式化(粗体,下划线)。应该是这样的:

带下划线的粗体文本

(注意斜体文本实际上是带下划线的,这里没有格式)

而是像这样返回:

带有 _u_n_d_e_r_l_i_n_e 的 BBoolldd 文本

我在http://cl.ly/052u2z2i2R280T3r1K3c有一个最小的测试项目,您可以下载并运行它;注意窗口什么都不做;输出被记录到控制台。

我想我需要以某种方式手动解释 NSData 对象,但我不知道从哪里开始。理想情况下,我希望将其转换为 NSAttributedString,但首要任务实际上是消除重复项和下划线。有什么想法吗?

4

3 回答 3

4

你的实际目的是什么?如果要显示手册页,一种选择是将其转换为 HTML 并使用 Web 视图呈现。

Parsingman的输出可能很棘手,因为默认情况下它是groff使用终端处理器处理的。这意味着输出被定制为在终端设备上显示。

一种替代解决方案是确定手册页源文件的实际位置,例如

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

并使用(ASCII 近似)和(禁用颜色输出)手动调用groff它,例如-a-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 -wgunzipgroff。默认配置文件是/private/etc/man.conf.

此外,您可以通过将适当的选项传递给grotty.

于 2011-01-26T20:42:11.463 回答
2

好的,这是我的解决方案的开始,尽管我会对任何其他(更简单的?)方法感兴趣。

从终端返回的输出是 UTF-8 编码,但 NSUTF8StringEncoding 不能正确解释字符串。原因是 NSTask 输出的格式化方式。

字母 N 在 UTF-8 中是 0x4e。但是对应的NSData是0x4e 0x08 0x4e。0x08 对应于退格键。因此,对于粗体字母,终端会打印 letter-backspace-letter。

对于斜体 c,它是 UTF-8 中的 0x63。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)];
    }
}];
于 2011-01-26T18:10:06.697 回答
1

另一种方法是将手册页转换为 PostScript 源代码,通过 PostScript-to-PDF 转换器运行,然后将其放入 PDFView。

实现将类似于 Bavarious 的答案,只是对 groff 有不同的参数(-Tps而不是-Thtml)。

这将是最慢的解决方案,但也可能是打印的最佳解决方案。

于 2011-01-27T11:51:37.043 回答