« アタッシェケース for iPhone ver1.1.3をリリースしました | トップページ | アタッシェケース Pro ver1.2.0をリリースしました »

2013年3月14日

圧縮RTF その2

圧縮RTFの続きです。先回の記事はこちら

先回説明した伸張アルゴリズムをObjectiv-Cでコーディング作ってみました。c++の書き方で書いているので,拡張子をmmにする必要があります。

/**
 * Compressed-RTFを伸張する
 */
- (NSString*) decompressRTF: (NSData*) data {
    @try {
        DICTIONARY dic = {
            "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\\colortbl\\red0\\green0\\blue0\r\n\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx",
            207,
            0,
            NO
        };
        NSMutableString* expandedString = [[NSMutableString alloc] init];
        NSInteger compSize = 0;         // 圧縮データ量
        [data getBytes:&compSize range: NSMakeRange(0, 4)];

        NSInteger rawSize = 0;          // 元データ量
        [data getBytes:&rawSize range: NSMakeRange(4, 4)];
        
        NSInteger pos = 16;    // 最初の16バイトはヘッダなので,初期値として16を設定

        while (pos < [data length]) {
            int control = 0;
            [data getBytes:&control range:NSMakeRange(pos++, 1)];   // 1バイト読み込んで,次の位置へ
            control = control & 0xff;
            //NSLog(@"control : %x", control);
            // dataからrunLength分を切り出す
            for (NSInteger i = 0; i < 8; i++) { // controlビット分だけループする
                unsigned char word[] = {0,0};
                if ((control & 0x01) != 0) {
                    // 最下位ビットが1
                    [data getBytes:&word range:NSMakeRange(pos, 2)];
                    pos += 2;
                    NSInteger offset = (word[0] & 0xff) * 16 + (word[1] & 0xff) / 16;  // ビックエンディアンで上位の12ビットがディクショナリのオフセット
                    NSInteger length = (word[1] & 0x0f)  + 2;         // ビックエンディアンで下位4ビットが長さ?2。?2されているので実際の長さは+2する必要あり
                    //NSLog(@"length %d  offset %d <-> writePos %d", length, offset, dic.writePos);
                    // offset が dictionaryへの書き込み位置と等しい場合は終了
                    if (offset == dic.writePos) {
                        pos = [data length];
                        break;
                    }
                    // dictionaryから指定した範囲の文字列を取ってくる。
                    while (length > 0) {
                        NSString* work = nil;
                        if (offset + length <= dic.writePos || dic.round) {
                            // 取り出す範囲がdictionaryの範囲内
                            work = [self readFromDictionay: &dic offset: offset length: length];
                            length = 0;
                        } else {
                            // 取り出す範囲がdictionaryに存在するデータの範囲を越えている場合
                            NSInteger workLength = dic.writePos - offset;
                            work = [self readFromDictionay: &dic offset: offset length: workLength];
                            offset += workLength;
                            length -= workLength;
                        }
                        // dictionary配列に文字列を追加する
                        [self appendToDictionary: &dic string: work];
                        [expandedString appendString: work];
                        work = nil;
                    }
                } else {
                    // 最下位ビットが0
                    [data getBytes:&word range:NSMakeRange(pos++, 1)];
                    // 文字リテラル
                    NSString* work = [NSString stringWithFormat:@"%c", word[0]];
                    // dictionaryに追加
                    [self appendToDictionary: &dic string: work];
                    [expandedString appendString: work];
                }
                control /= 2 ;
            }
            if ([expandedString length] >= rawSize) {
                break;
            }
        }
        //NSLog(@"%@", expandedString);
        return expandedString;
    }
    @catch (NSException *exception) {
        return nil;
    }
}

/*
 * Dictionaryに指定した文字列を書き込む。
 * Dictionaryは4096バイトのCircular bufferなので,そのことを考慮して書き込む必要がある。
 */
- (void) appendToDictionary: (DICTIONARY*) dic string: (NSString*) str {
    const char* cp = [str UTF8String];
    int length = [str lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
    for (int i = 0; i < length; i++) {
        dic->dictionary[dic->writePos] = cp[i];
        dic->writePos++;
        if (dic->round == NO && dic->writePos > 0xfff) {
            dic->round = YES;
        }
        dic->writePos = (dic->writePos & 0xfff);
    }
}

/*
 * Dictionaryから指定した文字列を取り出す。
 * Dictionaryは4096バイトのCircular bufferなので,そのことを考慮して取り出す必要がある。
 */
-(NSString*) readFromDictionay:(DICTIONARY*) dic offset:(NSInteger) offset length:(NSInteger) length {
    char* subArray = new char[length + 1];
    memset(subArray, 0, length + 1);
    for (NSInteger i = 0; i < length; i++) {
        subArray[i] = dic->dictionary[(offset + i) & 0xfff];
    }
    NSString* work = [NSString stringWithCString: subArray encoding:NSUTF8StringEncoding];
    delete[] subArray;

    return work;
}

NSDataオブジェクトに圧縮されたデータを格納して decompressRTF に渡してやれば,伸張できます。 バグなどお気づきの方は,コメントでお知らせいただけると助かります。

よろしくお願いいたします。

|

« アタッシェケース for iPhone ver1.1.3をリリースしました | トップページ | アタッシェケース Pro ver1.2.0をリリースしました »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/90513/56952203

この記事へのトラックバック一覧です: 圧縮RTF その2:

« アタッシェケース for iPhone ver1.1.3をリリースしました | トップページ | アタッシェケース Pro ver1.2.0をリリースしました »