// write a compressed text chunk void png_write_zTXt(string key, string text, PNG_TEXT_COMPRESSION compression) { uint key_len; byte[] new_key=null; if((key_len=png_check_keyword(key, ref new_key))==0) return; if(text==null||text.Length==0||compression==PNG_TEXT_COMPRESSION.NONE) { png_write_tEXt(key, text); return; } compression_state comp; comp.output_ptr=null; comp.input=null; // compute the compressed data; do it now for the length uint text_len=png_text_compress(Encoding.ASCII.GetBytes(text), compression, ref comp); // write start of chunk png_write_chunk_start(PNG.zTXt, key_len+text_len+2); new_key[key_len+1]=(byte)compression; // write key and compression png_write_chunk_data(new_key, key_len+2); // write the compressed data png_write_compressed_data_out(ref comp); // close the chunk png_write_chunk_end(); }
// write an iTXt chunk void png_write_iTXt(PNG_TEXT_COMPRESSION compression, string key, string lang, string lang_key, string text) { uint lang_len, key_len; byte[] new_lang=null, new_key=null; if((key_len=png_check_keyword(key, ref new_key))==0) return; if((lang_len=png_check_keyword(lang, ref new_lang))==0) { Debug.WriteLine("Empty language field in iTXt chunk"); new_lang=null; lang_len=0; } uint lang_key_len=0; if(lang_key!=null) lang_key_len=(uint)lang_key.Length; uint text_len=0; if(text!=null) text_len=(uint)text.Length; compression_state comp; comp.output_ptr=null; comp.input=null; // compute the compressed data; do it now for the length text_len=png_text_compress(text_len==0?null:Encoding.UTF8.GetBytes(text), compression-2, ref comp); // make sure we include the compression flag, the compression byte, // and the NULs after the key, lang, and lang_key parts png_write_chunk_start(PNG.iTXt, 5+key_len+lang_len+lang_key_len+text_len); // 5: comp byte, comp flag, terminators for key, lang and lang_key // We leave it to the application to meet PNG-1.0 requirements on the // contents of the text. PNG-1.0 through PNG-1.2 discourage the use of // any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. // The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. png_write_chunk_data(new_key, key_len+1); byte[] cbuf=new byte[2]; // set the compression flag if(compression==PNG_TEXT_COMPRESSION.ITXT_COMPRESSION_NONE||compression==PNG_TEXT_COMPRESSION.NONE) cbuf[0]=0; else cbuf[0]=1; // compression==PNG_TEXT_COMPRESSION.ITXT_COMPRESSION_zTXt // set the compression method cbuf[1]=0; png_write_chunk_data(cbuf, 2); cbuf[0]=0; png_write_chunk_data((new_lang!=null?new_lang:cbuf), lang_len+1); png_write_chunk_data((lang_key!=null?Encoding.UTF8.GetBytes(lang_key):cbuf), lang_key_len+1); png_write_compressed_data_out(ref comp); png_write_chunk_end(); }
// compress given text into storage in the png_ptr structure uint png_text_compress(byte[] text, PNG_TEXT_COMPRESSION compression, ref compression_state comp) { comp.input=null; comp.output_ptr=null; // we may just want to pass the text right through if(compression==PNG_TEXT_COMPRESSION.NONE) { comp.input=text; return (uint)text.Length; } if(compression>=PNG_TEXT_COMPRESSION.LAST) Debug.WriteLine("Unknown compression type "+(int)compression); // We can't write the chunk until we find out how much data we have, // which means we need to run the compressor first and save the // output. This shouldn't be a problem, as the vast majority of // comments should be reasonable, but we will set up an array of // malloc'd pointers to be sure. // // If we knew the application was well behaved, we could simplify this // greatly by assuming we can always malloc an output buffer large // enough to hold the compressed text ((1001*text_len/1000)+12) // and malloc this directly. The only time this would be a bad idea is // if we can't malloc more than 64K and we have 64K of random input // data, or if the input string is incredibly large (although this // wouldn't cause a failure, just a slowdown due to swapping). // set up the compression buffers zstream.avail_in=(uint)text.Length; zstream.next_in=0; zstream.in_buf=text; zstream.avail_out=zbuf_size; zstream.next_out=0; zstream.out_buf=zbuf; comp.output_ptr=new List<byte[]>(); // this is the same compression loop as in png_write_row() int ret; do { // compress the data ret=zlib.deflate(zstream, zlib.Z_NO_FLUSH); if(ret!=zlib.Z_OK) { // error if(zstream.msg!=null&&zstream.msg.Length>0) throw new PNG_Exception(zstream.msg); throw new PNG_Exception("zlib error"); } // check to see if we need more room if(zstream.avail_out==0) { // save the data byte[] buf=new byte[zbuf_size]; zbuf.CopyTo(buf, 0); comp.output_ptr.Add(buf); // and reset the buffer zstream.avail_out=zbuf_size; zstream.next_out=0; zstream.out_buf=zbuf; } // continue until we don't have any more to compress } while(zstream.avail_in!=0); // finish the compression do { // tell zlib we are finished ret=zlib.deflate(zstream, zlib.Z_FINISH); if(ret!=zlib.Z_STREAM_END) { // we got an error if(zstream.msg!=null&&zstream.msg.Length>0) throw new PNG_Exception(zstream.msg); throw new PNG_Exception("zlib error"); } if(ret==zlib.Z_OK) { // check to see if we need more room if(zstream.avail_out==0) { // save off the data byte[] buf=new byte[zbuf_size]; zbuf.CopyTo(buf, 0); comp.output_ptr.Add(buf); // and reset the buffer pointers zstream.avail_out=zbuf_size; zstream.next_out=0; zstream.out_buf=zbuf; } } } while(ret!=zlib.Z_STREAM_END); // text length is number of buffers plus last buffer uint text_len=zbuf_size*(uint)comp.output_ptr.Count; if(zstream.avail_out<zbuf_size) text_len+=zbuf_size-zstream.avail_out; return text_len; }