github.com/phpdave11/gofpdf@v1.4.2/png.go (about) 1 /* 2 * Copyright (c) 2013-2016 Kurt Jung (Gmail: kurt.w.jung) 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 package gofpdf 18 19 import ( 20 "bytes" 21 "fmt" 22 "strings" 23 ) 24 25 func (f *Fpdf) pngColorSpace(ct byte) (colspace string, colorVal int) { 26 colorVal = 1 27 switch ct { 28 case 0, 4: 29 colspace = "DeviceGray" 30 case 2, 6: 31 colspace = "DeviceRGB" 32 colorVal = 3 33 case 3: 34 colspace = "Indexed" 35 default: 36 f.err = fmt.Errorf("unknown color type in PNG buffer: %d", ct) 37 } 38 return 39 } 40 41 func (f *Fpdf) parsepngstream(buf *bytes.Buffer, readdpi bool) (info *ImageInfoType) { 42 info = f.newImageInfo() 43 // Check signature 44 if string(buf.Next(8)) != "\x89PNG\x0d\x0a\x1a\x0a" { 45 f.err = fmt.Errorf("not a PNG buffer") 46 return 47 } 48 // Read header chunk 49 _ = buf.Next(4) 50 if string(buf.Next(4)) != "IHDR" { 51 f.err = fmt.Errorf("incorrect PNG buffer") 52 return 53 } 54 w := f.readBeInt32(buf) 55 h := f.readBeInt32(buf) 56 bpc := f.readByte(buf) 57 if bpc > 8 { 58 f.err = fmt.Errorf("16-bit depth not supported in PNG file") 59 } 60 ct := f.readByte(buf) 61 var colspace string 62 var colorVal int 63 colspace, colorVal = f.pngColorSpace(ct) 64 if f.err != nil { 65 return 66 } 67 if f.readByte(buf) != 0 { 68 f.err = fmt.Errorf("'unknown compression method in PNG buffer") 69 return 70 } 71 if f.readByte(buf) != 0 { 72 f.err = fmt.Errorf("'unknown filter method in PNG buffer") 73 return 74 } 75 if f.readByte(buf) != 0 { 76 f.err = fmt.Errorf("interlacing not supported in PNG buffer") 77 return 78 } 79 _ = buf.Next(4) 80 dp := sprintf("/Predictor 15 /Colors %d /BitsPerComponent %d /Columns %d", colorVal, bpc, w) 81 // Scan chunks looking for palette, transparency and image data 82 pal := make([]byte, 0, 32) 83 var trns []int 84 data := make([]byte, 0, 32) 85 loop := true 86 for loop { 87 n := int(f.readBeInt32(buf)) 88 // dbg("Loop [%d]", n) 89 switch string(buf.Next(4)) { 90 case "PLTE": 91 // dbg("PLTE") 92 // Read palette 93 pal = buf.Next(n) 94 _ = buf.Next(4) 95 case "tRNS": 96 // dbg("tRNS") 97 // Read transparency info 98 t := buf.Next(n) 99 switch ct { 100 case 0: 101 trns = []int{int(t[1])} // ord(substr($t,1,1))); 102 case 2: 103 trns = []int{int(t[1]), int(t[3]), int(t[5])} // array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1))); 104 default: 105 pos := strings.Index(string(t), "\x00") 106 if pos >= 0 { 107 trns = []int{pos} // array($pos); 108 } 109 } 110 _ = buf.Next(4) 111 case "IDAT": 112 // dbg("IDAT") 113 // Read image data block 114 data = append(data, buf.Next(n)...) 115 _ = buf.Next(4) 116 case "IEND": 117 // dbg("IEND") 118 loop = false 119 case "pHYs": 120 // dbg("pHYs") 121 // png files theoretically support different x/y dpi 122 // but we ignore files like this 123 // but if they're the same then we can stamp our info 124 // object with it 125 x := int(f.readBeInt32(buf)) 126 y := int(f.readBeInt32(buf)) 127 units := buf.Next(1)[0] 128 // fmt.Printf("got a pHYs block, x=%d, y=%d, u=%d, readdpi=%t\n", 129 // x, y, int(units), readdpi) 130 // only modify the info block if the user wants us to 131 if x == y && readdpi { 132 switch units { 133 // if units is 1 then measurement is px/meter 134 case 1: 135 info.dpi = float64(x) / 39.3701 // inches per meter 136 default: 137 info.dpi = float64(x) 138 } 139 } 140 _ = buf.Next(4) 141 default: 142 // dbg("default") 143 _ = buf.Next(n + 4) 144 } 145 if loop { 146 loop = n > 0 147 } 148 } 149 if colspace == "Indexed" && len(pal) == 0 { 150 f.err = fmt.Errorf("missing palette in PNG buffer") 151 } 152 info.w = float64(w) 153 info.h = float64(h) 154 info.cs = colspace 155 info.bpc = int(bpc) 156 info.f = "FlateDecode" 157 info.dp = dp 158 info.pal = pal 159 info.trns = trns 160 // dbg("ct [%d]", ct) 161 if ct >= 4 { 162 // Separate alpha and color channels 163 var err error 164 data, err = sliceUncompress(data) 165 if err != nil { 166 f.err = err 167 return 168 } 169 var color, alpha bytes.Buffer 170 if ct == 4 { 171 // Gray image 172 width := int(w) 173 height := int(h) 174 length := 2 * width 175 var pos, elPos int 176 for i := 0; i < height; i++ { 177 pos = (1 + length) * i 178 color.WriteByte(data[pos]) 179 alpha.WriteByte(data[pos]) 180 elPos = pos + 1 181 for k := 0; k < width; k++ { 182 color.WriteByte(data[elPos]) 183 alpha.WriteByte(data[elPos+1]) 184 elPos += 2 185 } 186 } 187 } else { 188 // RGB image 189 width := int(w) 190 height := int(h) 191 length := 4 * width 192 var pos, elPos int 193 for i := 0; i < height; i++ { 194 pos = (1 + length) * i 195 color.WriteByte(data[pos]) 196 alpha.WriteByte(data[pos]) 197 elPos = pos + 1 198 for k := 0; k < width; k++ { 199 color.Write(data[elPos : elPos+3]) 200 alpha.WriteByte(data[elPos+3]) 201 elPos += 4 202 } 203 } 204 } 205 data = sliceCompress(color.Bytes()) 206 info.smask = sliceCompress(alpha.Bytes()) 207 if f.pdfVersion < "1.4" { 208 f.pdfVersion = "1.4" 209 } 210 } 211 info.data = data 212 return 213 }