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
17package gofpdf
18
19import (
20	"bytes"
21	"fmt"
22	"strings"
23)
24
25func (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
41func (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}
214