3

私は他の質問の解決策に取り組んでいますPNGの「zTXt」チャンクのデータを読み取っています。私は、ファイル内のチャンクを見つけて、zTXt のキーワードを読んでいます。zTXt の圧縮部分の読み取りに問題があります。以前に DeflateStream オブジェクトを使用したことがなく、問題が発生しています。読み取り時に、長さパラメーターが「非圧縮」バイトであることを期待しているようです。ただし、私の場合、「圧縮された」バイト単位のデータの長さしかわかりません。うまくいけばこれを回避するために、解凍する必要のあるすべてのデータを MemoryStream に入れ、DeflateStream で「読み取りから終了」します。「ブロックの長さがその補数と一致しません」というメッセージとともに InvalidDataException をスローすることを除いて、これは単なるピーチです。今、私はこれが何を意味するのか分かりません。

チャンクの形式は、ID ("zTXt") の 4 バイト、データ長のビッグ エンディアン 32 ビット int、データ、そして最後に今のところ無視している CRC32 チェックサムです。

zTXt チャンクの形式は、最初は null で終了し (キーワードとしての文字列)、次に圧縮方法 (常に 0、DEFLATE 方法) 用の 1 バイトで、残りのデータは圧縮されたテキストです。

私のメソッドは新しい FileStream を取り込み、zTXt キーワードとデータを含む辞書を返します。

これが今のモンスターです:

public static List<KeyValuePair<string, string>> GetZtxt(FileStream stream)
{
    var ret = new List<KeyValuePair<string, string>>();
    try {
        stream.Position = 0;
        var br = new BinaryReader(stream, Encoding.ASCII);
        var head = br.ReadBytes(8); // The header is the same for all PNGs.
        if (!head.SequenceEqual(new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A })) return null; // Not a PNG.
        while (stream.Position < stream.Length) {
            int len; // Length of chunk data.
            if (BitConverter.IsLittleEndian)
                len = BitConverter.ToInt32(br.ReadBytes(4).Reverse().ToArray(), 0);
            else
                len = br.ReadInt32();

            char[] cName = br.ReadChars(4); // The chunk type.
            if (cName.SequenceEqual(new[] { 'z', 'T', 'X', 't' })) {
                var sb = new StringBuilder(); // Builds the null-terminated keyword associated with the chunk.
                char c = br.ReadChar();
                do {
                    sb.Append(c);
                    c = br.ReadChar();
                }
                while (c != '\0');
                byte method = br.ReadByte(); // The compression method.  Should always be 0. (DEFLATE method.)
                if (method != 0) {
                    stream.Seek(len - sb.Length + 3, SeekOrigin.Current); // If not 0, skip the rest of the chunk.
                    continue;
                }
                var data = br.ReadBytes(len - sb.Length - 1); // Rest of the chunk data...
                var ms = new MemoryStream(data, 0, data.Length); // ...in a MemoryStream...
                var ds = new DeflateStream(ms, CompressionMode.Decompress); // ...read by a DeflateStream...
                var sr = new StreamReader(ds); // ... and a StreamReader.  Yeesh.
                var str = sr.ReadToEnd(); // !!! InvalidDataException !!!
                ret.Add(new KeyValuePair<string, string>(sb.ToString(), str));
                stream.Seek(4, SeekOrigin.Current); // Skip the CRC check.
            }
            else {
                stream.Seek(len + 4, SeekOrigin.Current); // Skip the rest of the chunk.
            }
        }
    }
    catch (IOException) { }
    catch (InvalidDataException) { }
    catch (ArgumentOutOfRangeException) { }
    return ret;
}

これに取り組んだら、これらの zTXt チャンクをファイルに追加する関数を作成する必要があります。これが解決されたら、DeflateStream がどのように機能するかを理解できることを願っています。

どうもありがとう!!

4

1 に答える 1

7

この間ずっと、私はついに問題を見つけました。データは zlib 形式であり、DEFLATE を単独で使用するよりも少し多くのデータが格納されています。圧縮データを取得する直前に余分な 2 バイトを読み込むと、ファイルは正しく読み込まれます。

このフィードバック ページを参照してください。(私はそれを提出しませんでした。)

私は今疑問に思っています。これらの 2 バイトの値は、それぞれ 0x78 と 0x9C です。これら以外の値を見つけた場合、DEFLATE が失敗すると想定する必要がありますか?

于 2009-05-21T23:59:46.350 に答える