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  }