png.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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. package gofpdf
  17. import (
  18. "bytes"
  19. "fmt"
  20. "strings"
  21. )
  22. func (f *Fpdf) pngColorSpace(ct byte) (colspace string, colorVal int) {
  23. colorVal = 1
  24. switch ct {
  25. case 0, 4:
  26. colspace = "DeviceGray"
  27. case 2, 6:
  28. colspace = "DeviceRGB"
  29. colorVal = 3
  30. case 3:
  31. colspace = "Indexed"
  32. default:
  33. f.err = fmt.Errorf("unknown color type in PNG buffer: %d", ct)
  34. }
  35. return
  36. }
  37. func (f *Fpdf) parsepngstream(buf *bytes.Buffer, readdpi bool) (info *ImageInfoType) {
  38. info = f.newImageInfo()
  39. // Check signature
  40. if string(buf.Next(8)) != "\x89PNG\x0d\x0a\x1a\x0a" {
  41. f.err = fmt.Errorf("not a PNG buffer")
  42. return
  43. }
  44. // Read header chunk
  45. _ = buf.Next(4)
  46. if string(buf.Next(4)) != "IHDR" {
  47. f.err = fmt.Errorf("incorrect PNG buffer")
  48. return
  49. }
  50. w := f.readBeInt32(buf)
  51. h := f.readBeInt32(buf)
  52. bpc := f.readByte(buf)
  53. if bpc > 8 {
  54. f.err = fmt.Errorf("16-bit depth not supported in PNG file")
  55. }
  56. ct := f.readByte(buf)
  57. var colspace string
  58. var colorVal int
  59. colspace, colorVal = f.pngColorSpace(ct)
  60. if f.err != nil {
  61. return
  62. }
  63. if f.readByte(buf) != 0 {
  64. f.err = fmt.Errorf("'unknown compression method in PNG buffer")
  65. return
  66. }
  67. if f.readByte(buf) != 0 {
  68. f.err = fmt.Errorf("'unknown filter method in PNG buffer")
  69. return
  70. }
  71. if f.readByte(buf) != 0 {
  72. f.err = fmt.Errorf("interlacing not supported in PNG buffer")
  73. return
  74. }
  75. _ = buf.Next(4)
  76. dp := sprintf("/Predictor 15 /Colors %d /BitsPerComponent %d /Columns %d", colorVal, bpc, w)
  77. // Scan chunks looking for palette, transparency and image data
  78. pal := make([]byte, 0, 32)
  79. var trns []int
  80. data := make([]byte, 0, 32)
  81. loop := true
  82. for loop {
  83. n := int(f.readBeInt32(buf))
  84. // dbg("Loop [%d]", n)
  85. switch string(buf.Next(4)) {
  86. case "PLTE":
  87. // dbg("PLTE")
  88. // Read palette
  89. pal = buf.Next(n)
  90. _ = buf.Next(4)
  91. case "tRNS":
  92. // dbg("tRNS")
  93. // Read transparency info
  94. t := buf.Next(n)
  95. switch ct {
  96. case 0:
  97. trns = []int{int(t[1])} // ord(substr($t,1,1)));
  98. case 2:
  99. 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)));
  100. default:
  101. pos := strings.Index(string(t), "\x00")
  102. if pos >= 0 {
  103. trns = []int{pos} // array($pos);
  104. }
  105. }
  106. _ = buf.Next(4)
  107. case "IDAT":
  108. // dbg("IDAT")
  109. // Read image data block
  110. data = append(data, buf.Next(n)...)
  111. _ = buf.Next(4)
  112. case "IEND":
  113. // dbg("IEND")
  114. loop = false
  115. case "pHYs":
  116. // dbg("pHYs")
  117. // png files theoretically support different x/y dpi
  118. // but we ignore files like this
  119. // but if they're the same then we can stamp our info
  120. // object with it
  121. x := int(f.readBeInt32(buf))
  122. y := int(f.readBeInt32(buf))
  123. units := buf.Next(1)[0]
  124. // fmt.Printf("got a pHYs block, x=%d, y=%d, u=%d, readdpi=%t\n",
  125. // x, y, int(units), readdpi)
  126. // only modify the info block if the user wants us to
  127. if x == y && readdpi {
  128. switch units {
  129. // if units is 1 then measurement is px/meter
  130. case 1:
  131. info.dpi = float64(x) / 39.3701 // inches per meter
  132. default:
  133. info.dpi = float64(x)
  134. }
  135. }
  136. _ = buf.Next(4)
  137. default:
  138. // dbg("default")
  139. _ = buf.Next(n + 4)
  140. }
  141. if loop {
  142. loop = n > 0
  143. }
  144. }
  145. if colspace == "Indexed" && len(pal) == 0 {
  146. f.err = fmt.Errorf("missing palette in PNG buffer")
  147. }
  148. info.w = float64(w)
  149. info.h = float64(h)
  150. info.cs = colspace
  151. info.bpc = int(bpc)
  152. info.f = "FlateDecode"
  153. info.dp = dp
  154. info.pal = pal
  155. info.trns = trns
  156. // dbg("ct [%d]", ct)
  157. if ct >= 4 {
  158. // Separate alpha and color channels
  159. var err error
  160. data, err = sliceUncompress(data)
  161. if err != nil {
  162. f.err = err
  163. return
  164. }
  165. var color, alpha bytes.Buffer
  166. if ct == 4 {
  167. // Gray image
  168. width := int(w)
  169. height := int(h)
  170. length := 2 * width
  171. var pos, elPos int
  172. for i := 0; i < height; i++ {
  173. pos = (1 + length) * i
  174. color.WriteByte(data[pos])
  175. alpha.WriteByte(data[pos])
  176. elPos = pos + 1
  177. for k := 0; k < width; k++ {
  178. color.WriteByte(data[elPos])
  179. alpha.WriteByte(data[elPos+1])
  180. elPos += 2
  181. }
  182. }
  183. } else {
  184. // RGB image
  185. width := int(w)
  186. height := int(h)
  187. length := 4 * width
  188. var pos, elPos int
  189. for i := 0; i < height; i++ {
  190. pos = (1 + length) * i
  191. color.WriteByte(data[pos])
  192. alpha.WriteByte(data[pos])
  193. elPos = pos + 1
  194. for k := 0; k < width; k++ {
  195. color.Write(data[elPos : elPos+3])
  196. alpha.WriteByte(data[elPos+3])
  197. elPos += 4
  198. }
  199. }
  200. }
  201. data = sliceCompress(color.Bytes())
  202. info.smask = sliceCompress(alpha.Bytes())
  203. if f.pdfVersion < "1.4" {
  204. f.pdfVersion = "1.4"
  205. }
  206. }
  207. info.data = data
  208. return
  209. }