summaryrefslogtreecommitdiff
path: root/png.go
diff options
context:
space:
mode:
Diffstat (limited to 'png.go')
-rw-r--r--png.go212
1 files changed, 212 insertions, 0 deletions
diff --git a/png.go b/png.go
new file mode 100644
index 0000000..854b003
--- /dev/null
+++ b/png.go
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2013-2016 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gofpdf
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+)
+
+func (f *Fpdf) pngColorSpace(ct byte) (colspace string, colorVal int) {
+ colorVal = 1
+ switch ct {
+ case 0, 4:
+ colspace = "DeviceGray"
+ case 2, 6:
+ colspace = "DeviceRGB"
+ colorVal = 3
+ case 3:
+ colspace = "Indexed"
+ default:
+ f.err = fmt.Errorf("unknown color type in PNG buffer: %d", ct)
+ }
+ return
+}
+
+func (f *Fpdf) parsepngstream(buf *bytes.Buffer, readdpi bool) (info *ImageInfoType) {
+ info = f.newImageInfo()
+ // Check signature
+ if string(buf.Next(8)) != "\x89PNG\x0d\x0a\x1a\x0a" {
+ f.err = fmt.Errorf("not a PNG buffer")
+ return
+ }
+ // Read header chunk
+ _ = buf.Next(4)
+ if string(buf.Next(4)) != "IHDR" {
+ f.err = fmt.Errorf("incorrect PNG buffer")
+ return
+ }
+ w := f.readBeInt32(buf)
+ h := f.readBeInt32(buf)
+ bpc := f.readByte(buf)
+ if bpc > 8 {
+ f.err = fmt.Errorf("16-bit depth not supported in PNG file")
+ }
+ ct := f.readByte(buf)
+ var colspace string
+ var colorVal int
+ colspace, colorVal = f.pngColorSpace(ct)
+ if f.err != nil {
+ return
+ }
+ if f.readByte(buf) != 0 {
+ f.err = fmt.Errorf("'unknown compression method in PNG buffer")
+ return
+ }
+ if f.readByte(buf) != 0 {
+ f.err = fmt.Errorf("'unknown filter method in PNG buffer")
+ return
+ }
+ if f.readByte(buf) != 0 {
+ f.err = fmt.Errorf("interlacing not supported in PNG buffer")
+ return
+ }
+ _ = buf.Next(4)
+ dp := sprintf("/Predictor 15 /Colors %d /BitsPerComponent %d /Columns %d", colorVal, bpc, w)
+ // Scan chunks looking for palette, transparency and image data
+ pal := make([]byte, 0, 32)
+ var trns []int
+ data := make([]byte, 0, 32)
+ loop := true
+ for loop {
+ n := int(f.readBeInt32(buf))
+ // dbg("Loop [%d]", n)
+ switch string(buf.Next(4)) {
+ case "PLTE":
+ // dbg("PLTE")
+ // Read palette
+ pal = buf.Next(n)
+ _ = buf.Next(4)
+ case "tRNS":
+ // dbg("tRNS")
+ // Read transparency info
+ t := buf.Next(n)
+ if ct == 0 {
+ trns = []int{int(t[1])} // ord(substr($t,1,1)));
+ } else if ct == 2 {
+ 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)));
+ } else {
+ pos := strings.Index(string(t), "\x00")
+ if pos >= 0 {
+ trns = []int{pos} // array($pos);
+ }
+ }
+ _ = buf.Next(4)
+ case "IDAT":
+ // dbg("IDAT")
+ // Read image data block
+ data = append(data, buf.Next(n)...)
+ _ = buf.Next(4)
+ case "IEND":
+ // dbg("IEND")
+ loop = false
+ case "pHYs":
+ // dbg("pHYs")
+ // png files theoretically support different x/y dpi
+ // but we ignore files like this
+ // but if they're the same then we can stamp our info
+ // object with it
+ x := int(f.readBeInt32(buf))
+ y := int(f.readBeInt32(buf))
+ units := buf.Next(1)[0]
+ // fmt.Printf("got a pHYs block, x=%d, y=%d, u=%d, readdpi=%t\n",
+ // x, y, int(units), readdpi)
+ // only modify the info block if the user wants us to
+ if x == y && readdpi {
+ switch units {
+ // if units is 1 then measurement is px/meter
+ case 1:
+ info.dpi = float64(x) / 39.3701 // inches per meter
+ default:
+ info.dpi = float64(x)
+ }
+ }
+ _ = buf.Next(4)
+ default:
+ // dbg("default")
+ _ = buf.Next(n + 4)
+ }
+ if loop {
+ loop = n > 0
+ }
+ }
+ if colspace == "Indexed" && len(pal) == 0 {
+ f.err = fmt.Errorf("missing palette in PNG buffer")
+ }
+ info.w = float64(w)
+ info.h = float64(h)
+ info.cs = colspace
+ info.bpc = int(bpc)
+ info.f = "FlateDecode"
+ info.dp = dp
+ info.pal = pal
+ info.trns = trns
+ // dbg("ct [%d]", ct)
+ if ct >= 4 {
+ // Separate alpha and color channels
+ var err error
+ data, err = sliceUncompress(data)
+ if err != nil {
+ f.err = err
+ return
+ }
+ var color, alpha bytes.Buffer
+ if ct == 4 {
+ // Gray image
+ width := int(w)
+ height := int(h)
+ length := 2 * width
+ var pos, elPos int
+ for i := 0; i < height; i++ {
+ pos = (1 + length) * i
+ color.WriteByte(data[pos])
+ alpha.WriteByte(data[pos])
+ elPos = pos + 1
+ for k := 0; k < width; k++ {
+ color.WriteByte(data[elPos])
+ alpha.WriteByte(data[elPos+1])
+ elPos += 2
+ }
+ }
+ } else {
+ // RGB image
+ width := int(w)
+ height := int(h)
+ length := 4 * width
+ var pos, elPos int
+ for i := 0; i < height; i++ {
+ pos = (1 + length) * i
+ color.WriteByte(data[pos])
+ alpha.WriteByte(data[pos])
+ elPos = pos + 1
+ for k := 0; k < width; k++ {
+ color.Write(data[elPos : elPos+3])
+ alpha.WriteByte(data[elPos+3])
+ elPos += 4
+ }
+ }
+ }
+ data = sliceCompress(color.Bytes())
+ info.smask = sliceCompress(alpha.Bytes())
+ if f.pdfVersion < "1.4" {
+ f.pdfVersion = "1.4"
+ }
+ }
+ info.data = data
+ return
+}