5 // Larry Ewing <lewing@novell.com>
8 // Copyright (C) 2004 - 2006 Novell, Inc (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using ICSharpCode
.SharpZipLib
.Zip
.Compression
;
33 namespace Beagle
.Util
{
34 public class PngHeader
{
35 System
.Collections
.ArrayList chunk_list
;
37 public PngHeader (System
.IO
.Stream stream
)
43 Title Short (one line) title or caption for image
44 Author Name of image's creator
45 Description Description of image (possibly long)
46 Copyright Copyright notice
47 Creation Time Time of original image creation
48 Software Software used to create the image
49 Disclaimer Legal disclaimer
50 Warning Warning of nature of content
51 Source Device used to create the image
52 Comment Miscellaneous comment
54 xmp is XML:com.adobe.xmp
56 Other keywords may be defined for other purposes. Keywords of general interest can be registered with th
60 public void Select (SemWeb.StatementSink sink)
62 foreach (Chunk c in Chunks) {
64 IhdrChunk ih = c as IhdrChunk;
65 MetadataStore.AddLiteral (sink, "tiff:ImageWidth", ih.Width.ToString ());
66 MetadataStore.AddLiteral (sink, "tiff:ImageLength", ih.Height.ToString ());
67 } else if(c is TimeChunk) {
68 TimeChunk tc = c as TimeChunk;
70 MetadataStore.AddLiteral (sink, "xmp:ModifyDate", tc.Time.ToString ("yyyy-MM-ddThh:mm:ss"));
71 } else if (c is TextChunk) {
72 TextChunk text = c as TextChunk;
74 switch (text.Keyword) {
76 case "XML:com.adobe.xmp":
77 System.IO.Stream xmpstream = new System.IO.MemoryStream (text.TextData);
78 XmpFile xmp = new XmpFile (xmpstream);
82 MetadataStore.AddLiteral (sink, "exif:UserComment", text.Text);
87 MetadataStore.AddLiteral (sink, "dc:title", "rdf:Alt", new Literal (text.Text, "x-default", null));
90 MetadataStore.AddLiteral (sink, "dc:creator", "rdf:Seq", new Literal (text.Text));
93 MetadataStore.AddLiteral (sink, "dc:rights", "rdf:Alt", new Literal (text.Text, "x-default", null));
96 MetadataStore.AddLiteral (sink, "dc:description", "rdf:Alt", new Literal (text.Text, "x-default", null));
100 System.DateTime time = System.DateTime.Parse (text.Text);
101 MetadataStore.AddLiteral (sink, "xmp:CreateDate", time.ToString ("yyyy-MM-ddThh:mm:ss"));
102 } catch (System.Exception e) {
103 System.Console.WriteLine (e.ToString ());
107 } else if (c is ColorChunk) {
108 ColorChunk color = (ColorChunk)c;
109 string [] whitepoint = new string [2];
110 whitepoint [0] = color.WhiteX.ToString ();
111 whitepoint [1] = color.WhiteY.ToString ();
112 MetadataStore.Add (sink, "tiff:WhitePoint", "rdf:Seq", whitepoint);
114 string [] rgb = new string [6];
115 rgb [i++] = color.RedX.ToString ();
116 rgb [i++] = color.RedY.ToString ();
117 rgb [i++] = color.GreenX.ToString ();
118 rgb [i++] = color.GreenY.ToString ();
119 rgb [i++] = color.BlueX.ToString ();
120 rgb [i++] = color.BlueY.ToString ();
121 MetadataStore.Add (sink, "tiff:PrimaryChromaticities", "rdf:Seq", rgb);
122 } else if (c.Name == "sRGB") {
123 MetadataStore.AddLiteral (sink, "exif:ColorSpace", "1");
124 } else if (c is PhysChunk) {
125 PhysChunk phys = (PhysChunk)c;
126 uint denominator = (uint) (phys.InMeters ? 100 : 1);
128 MetadataStore.AddLiteral (sink, "tiff:ResolutionUnit", phys.InMeters ? "3" : "1");
129 MetadataStore.AddLiteral (sink, "tiff:XResolution", new FSpot.Tiff.Rational (phys.PixelsPerUnitX, denominator).ToString ());
130 MetadataStore.AddLiteral (sink, "tiff:YResolution", new FSpot.Tiff.Rational (phys.PixelsPerUnitY, denominator).ToString ());
136 public System
.Collections
.ArrayList Chunks
{
137 get { return chunk_list; }
140 public class ZtxtChunk
: TextChunk
{
141 //public static string Name = "zTXt";
143 protected bool compressed
= true;
144 public bool Compressed
{
151 public byte Compression
{
156 if (compression
!= 0)
157 throw new System
.Exception ("Unknown compression method");
161 public ZtxtChunk (string name
, byte [] data
) : base (name
, data
) {}
163 public override void Load (byte [] data
)
166 keyword
= GetString (ref i
);
168 Compression
= data
[i
++];
170 text_data
= Chunk
.Inflate (data
, i
, data
.Length
- i
);
174 public class PhysChunk
: Chunk
{
175 public PhysChunk (string name
, byte [] data
) : base (name
, data
) {}
177 public uint PixelsPerUnitX
{
179 return EndianConverter
.ToUInt32 (data
, 0, false);
183 public uint PixelsPerUnitY
{
185 return EndianConverter
.ToUInt32 (data
, 4, false);
189 public bool InMeters
{
191 return data
[8] == 0;
196 public class TextChunk
: Chunk
{
197 //public static string Name = "tEXt";
199 protected string keyword
;
200 protected string text
;
201 protected byte [] text_data
;
202 protected System
.Text
.Encoding encoding
= Latin1
;
204 public static System
.Text
.Encoding Latin1
= System
.Text
.Encoding
.GetEncoding (28591);
205 public TextChunk (string name
, byte [] data
) : base (name
, data
) {}
207 public override void Load (byte [] data
)
211 keyword
= GetString (ref i
);
213 int len
= data
.Length
- i
;
214 text_data
= new byte [len
];
215 System
.Array
.Copy (data
, i
, text_data
, 0, len
);
218 public string Keyword
{
224 public byte [] TextData
233 return encoding
.GetString (text_data
, 0, text_data
.Length
);
238 public class IccpChunk
: Chunk
{
242 public IccpChunk (string name
, byte [] data
) : base (name
, data
) {}
244 public override void Load (byte [] data
)
247 keyword
= GetString (ref i
);
249 int compression
= data
[i
++];
250 if (compression
!= 0)
251 throw new System
.Exception ("Unknown Compression type");
253 profile
= Chunk
.Inflate (data
, i
, data
.Length
- i
);
256 public string Keyword
{
262 public byte [] Profile
{
269 public class ItxtChunk
: ZtxtChunk
{
270 //public static string Name = "zTXt";
273 string LocalizedKeyword
;
275 public override void Load (byte [] data
)
278 keyword
= GetString (ref i
);
280 compressed
= (data
[i
++] != 0);
281 Compression
= data
[i
++];
282 Language
= GetString (ref i
);
284 LocalizedKeyword
= GetString (ref i
, System
.Text
.Encoding
.UTF8
);
288 text_data
= Chunk
.Inflate (data
, i
, data
.Length
- i
);
290 int len
= data
.Length
- i
;
291 text_data
= new byte [len
];
292 System
.Array
.Copy (data
, i
, text_data
, 0, len
);
296 public ItxtChunk (string name
, byte [] data
) : base (name
, data
)
298 encoding
= System
.Text
.Encoding
.UTF8
;
302 public class TimeChunk
: Chunk
{
303 //public static string Name = "tIME";
305 System
.DateTime time
;
307 public System
.DateTime Time
{
309 return new System
.DateTime (EndianConverter
.ToUInt16 (data
, 0, false),
310 data
[2], data
[3], data
[4], data
[5], data
[6]);
314 byte [] year
= EndianConverter
.GetBytes ((ushort)value.Year
, false);
317 data
[2] = (byte) value.Month
;
318 data
[3] = (byte) value.Day
;
319 data
[4] = (byte) value.Hour
;
320 data
[6] = (byte) value.Minute
;
321 data
[7] = (byte) value.Second
;
325 public TimeChunk (string name
, byte [] data
) : base (name
, data
) {}
328 public class StandardRgbChunk
: Chunk
{
329 public StandardRgbChunk (string name
, byte [] data
) : base (name
, data
) {}
332 public class GammaChunk
: Chunk
{
333 public GammaChunk (string name
, byte [] data
) : base (name
, data
) {}
334 private const int divisor
= 100000;
336 public double Gamma
{
338 return EndianConverter
.ToUInt32 (data
, 0, false) / (double) divisor
;
343 public class ColorChunk
: Chunk
{
344 // FIXME this should be represented like a tiff rational
345 public const uint Denominator
= 100000;
347 public ColorChunk (string name
, byte [] data
) : base (name
, data
) {}
349 public FSpot.Tiff.Rational WhiteX {
351 return new FSpot.Tiff.Rational (FSpot.EndianConverter.ToUInt32 (data, 0, false), Denominator);
354 public FSpot.Tiff.Rational WhiteY {
356 return new FSpot.Tiff.Rational (FSpot.EndianConverter.ToUInt32 (data, 4, false), Denominator);
359 public FSpot.Tiff.Rational RedX {
361 return new FSpot.Tiff.Rational (FSpot.EndianConverter.ToUInt32 (data, 8, false), Denominator);
364 public FSpot.Tiff.Rational RedY {
366 return new FSpot.Tiff.Rational (FSpot.EndianConverter.ToUInt32 (data, 12, false), Denominator);
369 public FSpot.Tiff.Rational GreenX {
371 return new FSpot.Tiff.Rational (FSpot.EndianConverter.ToUInt32 (data, 16, false), Denominator);
374 public FSpot.Tiff.Rational GreenY {
376 return new FSpot.Tiff.Rational (FSpot.EndianConverter.ToUInt32 (data, 20, false), Denominator);
379 public FSpot.Tiff.Rational BlueX {
381 return new FSpot.Tiff.Rational (FSpot.EndianConverter.ToUInt32 (data, 24, false), Denominator);
384 public FSpot.Tiff.Rational BlueY {
386 return new FSpot.Tiff.Rational (FSpot.EndianConverter.ToUInt32 (data, 28, false), Denominator);
392 public enum ColorType
: byte {
400 public enum CompressionMethod
: byte {
404 public enum InterlaceMethod
: byte {
409 public enum FilterMethod
: byte {
413 // Filter Types Show up as the first byte of each scanline
414 public enum FilterType
{
422 public class IhdrChunk
: Chunk
{
426 public ColorType Color
;
427 public PngHeader
.CompressionMethod Compression
;
428 public FilterMethod Filter
;
429 public InterlaceMethod Interlace
;
431 public IhdrChunk (string name
, byte [] data
) : base (name
, data
) {}
433 public override void Load (byte [] data
)
435 Width
= EndianConverter
.ToUInt32 (data
, 0, false);
436 Height
= EndianConverter
.ToUInt32 (data
, 4, false);
438 Color
= (ColorType
) data
[9];
439 //if (Color != ColorType.Rgb)
440 // throw new System.Exception (System.String.Format ("unsupported {0}", Color));
442 this.Compression
= (CompressionMethod
) data
[10];
443 if (this.Compression
!= CompressionMethod
.Zlib
)
444 throw new System
.Exception (System
.String
.Format ("unsupported {0}", Compression
));
446 Filter
= (FilterMethod
) data
[11];
447 if (Filter
!= FilterMethod
.Adaptive
)
448 throw new System
.Exception (System
.String
.Format ("unsupported {0}", Filter
));
450 Interlace
= (InterlaceMethod
) data
[12];
451 //if (Interlace != InterlaceMethod.None)
452 // throw new System.Exception (System.String.Format ("unsupported {0}", Interlace));
456 public int ScanlineComponents
{
460 case ColorType
.Indexed
:
462 case ColorType
.GrayAlpha
:
466 case ColorType
.RgbAlpha
:
469 throw new System
.Exception (System
.String
.Format ("Unknown format {0}", Color
));
474 public uint GetScanlineLength (int pass
)
477 if (Interlace
== InterlaceMethod
.None
) {
478 int bits
= ScanlineComponents
* Depth
;
479 length
= (uint) (this.Width
* bits
/ 8);
481 // and a byte for the FilterType
484 throw new System
.Exception (System
.String
.Format ("unsupported {0}", Interlace
));
493 protected byte [] data
;
494 protected static System
.Collections
.Hashtable name_table
;
496 public byte [] Data
{
508 name_table
= new System
.Collections
.Hashtable ();
509 name_table
["iTXt"] = typeof (ItxtChunk
);
510 name_table
["tXMP"] = typeof (ItxtChunk
);
511 name_table
["tEXt"] = typeof (TextChunk
);
512 name_table
["zTXt"] = typeof (ZtxtChunk
);
513 name_table
["tIME"] = typeof (TimeChunk
);
514 name_table
["iCCP"] = typeof (IccpChunk
);
515 name_table
["IHDR"] = typeof (IhdrChunk
);
516 name_table
["cHRM"] = typeof (ColorChunk
);
517 name_table
["pHYs"] = typeof (PhysChunk
);
518 name_table
["gAMA"] = typeof (GammaChunk
);
519 name_table
["sRGB"] = typeof (StandardRgbChunk
);
522 public Chunk (string name
, byte [] data
)
530 protected string GetString (ref int i
, System
.Text
.Encoding enc
)
532 for (; i
< data
.Length
; i
++) {
537 return enc
.GetString (data
, 0, i
);
540 protected string GetString (ref int i
)
542 return GetString (ref i
, TextChunk
.Latin1
);
545 public virtual void Load (byte [] data
)
550 public bool Critical
{
552 return !System
.Char
.IsLower (Name
, 0);
556 public bool Private
{
558 return System
.Char
.IsLower (Name
, 1);
562 public bool Reserved
{
564 return System
.Char
.IsLower (Name
, 2);
570 return System
.Char
.IsLower (Name
, 3);
574 public bool CheckCrc (uint crc
)
576 // FIXME implement me
580 public static Chunk
Generate (string name
, byte [] data
)
582 System
.Type t
= (System
.Type
) name_table
[name
];
586 chunk
= (Chunk
) System
.Activator
.CreateInstance (t
, new object[] {name, data}
);
588 chunk
= new Chunk (name
, data
);
593 public static byte [] Inflate (byte [] input
, int start
, int length
)
595 System
.IO
.MemoryStream output
= new System
.IO
.MemoryStream ();
596 Inflater inflater
= new Inflater ();
598 inflater
.SetInput (input
, start
, length
);
600 byte [] buf
= new byte [1024];
602 while ((inflate_length
= inflater
.Inflate (buf
)) > 0) {
603 output
.Write (buf
, 0, inflate_length
);
606 byte [] result
= new byte [output
.Length
];
608 output
.Read (result
, 0, result
.Length
);
615 public class ChunkInflater
{
616 private Inflater inflater
;
617 private System
.Collections
.ArrayList chunks
;
619 public ChunkInflater ()
621 inflater
= new Inflater ();
622 chunks
= new System
.Collections
.ArrayList ();
627 while (inflater
.IsNeedingInput
&& chunks
.Count
> 0) {
628 inflater
.SetInput (((Chunk
)chunks
[0]).Data
);
629 //System.Console.WriteLine ("adding chunk {0}", ((Chunk)chunks[0]).Data.Length);
635 public int Inflate (byte [] data
, int start
, int length
)
640 result
+= inflater
.Inflate (data
, start
+ result
, length
- result
);
641 //System.Console.WriteLine ("Attempting Second after fill Inflate {0} {1} {2}", attempt, result, length - result);
642 } while (result
< length
&& chunks
.Count
> 0);
647 public void Add (Chunk chunk
)
653 void Load (System
.IO
.Stream stream
)
655 byte [] heading
= new byte [8];
656 stream
.Read (heading
, 0, heading
.Length
);
658 if (heading
[0] != 137 ||
666 throw new System
.Exception ("Invalid PNG magic number");
668 chunk_list
= new System
.Collections
.ArrayList ();
670 for (int i
= 0; stream
.Read (heading
, 0, heading
.Length
) == heading
.Length
; i
++) {
671 uint length
= EndianConverter
.ToUInt32 (heading
, 0, false);
672 string name
= System
.Text
.Encoding
.ASCII
.GetString (heading
, 4, 4);
673 byte [] data
= new byte [length
];
675 stream
.Read (data
, 0, data
.Length
);
677 stream
.Read (heading
, 0, 4);
678 uint crc
= EndianConverter
.ToUInt32 (heading
, 0, false);
680 Chunk chunk
= Chunk
.Generate (name
, data
);
681 if (! chunk
.CheckCrc (crc
)) {
682 if (chunk
.Critical
) {
683 throw new System
.Exception ("Chunk CRC check failed");
685 System
.Console
.WriteLine ("bad CRC in Chunk {0}... skipping",
690 chunk_list
.Add (chunk
);
693 if (chunk
.Name
== "IEND")
698 public string LookupText (string keyword
)
700 TextChunk chunk
= LookupTextChunk (keyword
);
707 public TextChunk
LookupTextChunk (string keyword
)
709 foreach (Chunk chunk
in Chunks
) {
710 TextChunk text
= chunk
as TextChunk
;
711 if (text
!= null && text
.Keyword
== keyword
)