BCGDrawPNG.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <?php
  2. /**
  3. *--------------------------------------------------------------------
  4. *
  5. * Image Class to draw PNG images with possibility to set DPI
  6. *
  7. *--------------------------------------------------------------------
  8. * Copyright (C) Jean-Sebastien Goupil
  9. * http://www.barcodephp.com
  10. */
  11. include_once('BCGDraw.php');
  12. if (!function_exists('file_put_contents')) {
  13. function file_put_contents($filename, $data) {
  14. $f = @fopen($filename, 'w');
  15. if (!$f) {
  16. return false;
  17. } else {
  18. $bytes = fwrite($f, $data);
  19. fclose($f);
  20. return $bytes;
  21. }
  22. }
  23. }
  24. class BCGDrawPNG extends BCGDraw {
  25. private $dpi;
  26. /**
  27. * Constructor.
  28. *
  29. * @param resource $im
  30. */
  31. public function __construct($im) {
  32. parent::__construct($im);
  33. }
  34. /**
  35. * Sets the DPI.
  36. *
  37. * @param int $dpi
  38. */
  39. public function setDPI($dpi) {
  40. if (is_numeric($dpi)) {
  41. $this->dpi = max(1, $dpi);
  42. } else {
  43. $this->dpi = null;
  44. }
  45. }
  46. /**
  47. * Draws the PNG on the screen or in a file.
  48. */
  49. public function draw() {
  50. ob_start();
  51. imagepng($this->im);
  52. $bin = ob_get_contents();
  53. ob_end_clean();
  54. $this->setInternalProperties($bin);
  55. if (empty($this->filename)) {
  56. echo $bin;
  57. } else {
  58. file_put_contents($this->filename, $bin);
  59. }
  60. }
  61. private function setInternalProperties(&$bin) {
  62. // Scan all the ChunkType
  63. if (strcmp(substr($bin, 0, 8), pack('H*', '89504E470D0A1A0A')) === 0) {
  64. $chunks = $this->detectChunks($bin);
  65. $this->internalSetDPI($bin, $chunks);
  66. $this->internalSetC($bin, $chunks);
  67. }
  68. }
  69. private function detectChunks($bin) {
  70. $data = substr($bin, 8);
  71. $chunks = array();
  72. $c = strlen($data);
  73. $offset = 0;
  74. while ($offset < $c) {
  75. $packed = unpack('Nsize/a4chunk', $data);
  76. $size = $packed['size'];
  77. $chunk = $packed['chunk'];
  78. $chunks[] = array('offset' => $offset + 8, 'size' => $size, 'chunk' => $chunk);
  79. $jump = $size + 12;
  80. $offset += $jump;
  81. $data = substr($data, $jump);
  82. }
  83. return $chunks;
  84. }
  85. private function internalSetDPI(&$bin, &$chunks) {
  86. if ($this->dpi !== null) {
  87. $meters = (int)($this->dpi * 39.37007874);
  88. $found = -1;
  89. $c = count($chunks);
  90. for($i = 0; $i < $c; $i++) {
  91. // We already have a pHYs
  92. if($chunks[$i]['chunk'] === 'pHYs') {
  93. $found = $i;
  94. break;
  95. }
  96. }
  97. $data = 'pHYs' . pack('NNC', $meters, $meters, 0x01);
  98. $crc = self::crc($data, 13);
  99. $cr = pack('Na13N', 9, $data, $crc);
  100. // We didn't have a pHYs
  101. if($found == -1) {
  102. // Don't do anything if we have a bad PNG
  103. if($c >= 2 && $chunks[0]['chunk'] === 'IHDR') {
  104. array_splice($chunks, 1, 0, array(array('offset' => 33, 'size' => 9, 'chunk' => 'pHYs')));
  105. // Push the data
  106. for($i = 2; $i < $c; $i++) {
  107. $chunks[$i]['offset'] += 21;
  108. }
  109. $firstPart = substr($bin, 0, 33);
  110. $secondPart = substr($bin, 33);
  111. $bin = $firstPart;
  112. $bin .= $cr;
  113. $bin .= $secondPart;
  114. }
  115. } else {
  116. $bin = substr_replace($bin, $cr, $chunks[$i]['offset'], 21);
  117. }
  118. }
  119. }
  120. private function internalSetC(&$bin, &$chunks) {
  121. if (count($chunks) >= 2 && $chunks[0]['chunk'] === 'IHDR') {
  122. $firstPart = substr($bin, 0, 33);
  123. $secondPart = substr($bin, 33);
  124. $cr = pack('H*', '0000004C74455874436F707972696768740047656E657261746564207769746820426172636F64652047656E657261746F7220666F722050485020687474703A2F2F7777772E626172636F64657068702E636F6D597F70B8');
  125. $bin = $firstPart;
  126. $bin .= $cr;
  127. $bin .= $secondPart;
  128. }
  129. // Chunks is dirty!! But we are done.
  130. }
  131. private static $crc_table = array();
  132. private static $crc_table_computed = false;
  133. private static function make_crc_table() {
  134. for ($n = 0; $n < 256; $n++) {
  135. $c = $n;
  136. for ($k = 0; $k < 8; $k++) {
  137. if (($c & 1) == 1) {
  138. $c = 0xedb88320 ^ (self::SHR($c, 1));
  139. } else {
  140. $c = self::SHR($c, 1);
  141. }
  142. }
  143. self::$crc_table[$n] = $c;
  144. }
  145. self::$crc_table_computed = true;
  146. }
  147. private static function SHR($x, $n) {
  148. $mask = 0x40000000;
  149. if ($x < 0) {
  150. $x &= 0x7FFFFFFF;
  151. $mask = $mask >> ($n - 1);
  152. return ($x >> $n) | $mask;
  153. }
  154. return (int)$x >> (int)$n;
  155. }
  156. private static function update_crc($crc, $buf, $len) {
  157. $c = $crc;
  158. if (!self::$crc_table_computed) {
  159. self::make_crc_table();
  160. }
  161. for ($n = 0; $n < $len; $n++) {
  162. $c = self::$crc_table[($c ^ ord($buf[$n])) & 0xff] ^ (self::SHR($c, 8));
  163. }
  164. return $c;
  165. }
  166. private static function crc($data, $len) {
  167. return self::update_crc(-1, $data, $len) ^ -1;
  168. }
  169. }
  170. ?>