BCGupce.barcode.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <?php
  2. /**
  3. *--------------------------------------------------------------------
  4. *
  5. * Sub-Class - UPC-E
  6. *
  7. * You can provide a UPC-A code (without dash), the code will transform
  8. * it into a UPC-E format if it's possible.
  9. * UPC-E contains
  10. * - 1 system digits (not displayed but coded with parity)
  11. * - 6 digits
  12. * - 1 checksum digit (not displayed but coded with parity)
  13. *
  14. * The text returned is the UPC-E without the checksum.
  15. * The checksum is always displayed.
  16. *
  17. *--------------------------------------------------------------------
  18. * Copyright (C) Jean-Sebastien Goupil
  19. * http://www.barcodephp.com
  20. */
  21. include_once('BCGParseException.php');
  22. include_once('BCGBarcode.php');
  23. include_once('BCGBarcode1D.php');
  24. include_once('BCGLabel.php');
  25. class BCGupce extends BCGBarcode1D {
  26. protected $codeParity = array();
  27. protected $upce;
  28. protected $labelLeft = null;
  29. protected $labelCenter = null;
  30. protected $labelRight = null;
  31. /**
  32. * Constructor.
  33. */
  34. public function __construct() {
  35. parent::__construct();
  36. $this->keys = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
  37. // Odd Parity starting with a space
  38. // Even Parity is the inverse (0=0012) starting with a space
  39. $this->code = array(
  40. '2100', /* 0 */
  41. '1110', /* 1 */
  42. '1011', /* 2 */
  43. '0300', /* 3 */
  44. '0021', /* 4 */
  45. '0120', /* 5 */
  46. '0003', /* 6 */
  47. '0201', /* 7 */
  48. '0102', /* 8 */
  49. '2001' /* 9 */
  50. );
  51. // Parity, 0=Odd, 1=Even for manufacturer code. Depending on 1st System Digit and Checksum
  52. $this->codeParity = array(
  53. array(
  54. array(1, 1, 1, 0, 0, 0), /* 0,0 */
  55. array(1, 1, 0, 1, 0, 0), /* 0,1 */
  56. array(1, 1, 0, 0, 1, 0), /* 0,2 */
  57. array(1, 1, 0, 0, 0, 1), /* 0,3 */
  58. array(1, 0, 1, 1, 0, 0), /* 0,4 */
  59. array(1, 0, 0, 1, 1, 0), /* 0,5 */
  60. array(1, 0, 0, 0, 1, 1), /* 0,6 */
  61. array(1, 0, 1, 0, 1, 0), /* 0,7 */
  62. array(1, 0, 1, 0, 0, 1), /* 0,8 */
  63. array(1, 0, 0, 1, 0, 1) /* 0,9 */
  64. ),
  65. array(
  66. array(0, 0, 0, 1, 1, 1), /* 0,0 */
  67. array(0, 0, 1, 0, 1, 1), /* 0,1 */
  68. array(0, 0, 1, 1, 0, 1), /* 0,2 */
  69. array(0, 0, 1, 1, 1, 0), /* 0,3 */
  70. array(0, 1, 0, 0, 1, 1), /* 0,4 */
  71. array(0, 1, 1, 0, 0, 1), /* 0,5 */
  72. array(0, 1, 1, 1, 0, 0), /* 0,6 */
  73. array(0, 1, 0, 1, 0, 1), /* 0,7 */
  74. array(0, 1, 0, 1, 1, 0), /* 0,8 */
  75. array(0, 1, 1, 0, 1, 0) /* 0,9 */
  76. )
  77. );
  78. }
  79. /**
  80. * Draws the barcode.
  81. *
  82. * @param resource $im
  83. */
  84. public function draw($im) {
  85. $this->calculateChecksum();
  86. // Starting Code
  87. $this->drawChar($im, '000', true);
  88. $c = strlen($this->upce);
  89. for ($i = 0; $i < $c; $i++) {
  90. $this->drawChar($im, self::inverse($this->findCode($this->upce[$i]), $this->codeParity[intval($this->text[0])][$this->checksumValue][$i]), false);
  91. }
  92. // Draw Center Guard Bar
  93. $this->drawChar($im, '00000', false);
  94. // Draw Right Bar
  95. $this->drawChar($im, '0', true);
  96. $this->text = $this->text[0] . $this->upce;
  97. $this->drawText($im, 0, 0, $this->positionX, $this->thickness);
  98. if ($this->isDefaultEanLabelEnabled()) {
  99. $dimension = $this->labelCenter->getDimension();
  100. $this->drawExtendedBars($im, $dimension[1] - 2);
  101. }
  102. }
  103. /**
  104. * Returns the maximal size of a barcode.
  105. *
  106. * @param int $w
  107. * @param int $h
  108. * @return int[]
  109. */
  110. public function getDimension($w, $h) {
  111. $startlength = 3;
  112. $centerlength = 5;
  113. $textlength = 6 * 7;
  114. $endlength = 1;
  115. $w += $startlength + $centerlength + $textlength + $endlength;
  116. $h += $this->thickness;
  117. return parent::getDimension($w, $h);
  118. }
  119. /**
  120. * Adds the default label.
  121. */
  122. protected function addDefaultLabel() {
  123. if ($this->isDefaultEanLabelEnabled()) {
  124. $this->processChecksum();
  125. $font = $this->font;
  126. $this->labelLeft = new BCGLabel(substr($this->text, 0, 1), $font, BCGLabel::POSITION_LEFT, BCGLabel::ALIGN_BOTTOM);
  127. $labelLeftDimension = $this->labelLeft->getDimension();
  128. $this->labelLeft->setSpacing(8);
  129. $this->labelLeft->setOffset($labelLeftDimension[1] / 2);
  130. $this->labelCenter = new BCGLabel($this->upce, $font, BCGLabel::POSITION_BOTTOM, BCGLabel::ALIGN_LEFT);
  131. $labelCenterDimension = $this->labelCenter->getDimension();
  132. $this->labelCenter->setOffset(($this->scale * 46 - $labelCenterDimension[0]) / 2 + $this->scale * 2);
  133. $this->labelRight = new BCGLabel($this->keys[$this->checksumValue], $font, BCGLabel::POSITION_RIGHT, BCGLabel::ALIGN_BOTTOM);
  134. $labelRightDimension = $this->labelRight->getDimension();
  135. $this->labelRight->setSpacing(8);
  136. $this->labelRight->setOffset($labelRightDimension[1] / 2);
  137. $this->addLabel($this->labelLeft);
  138. $this->addLabel($this->labelCenter);
  139. $this->addLabel($this->labelRight);
  140. }
  141. }
  142. /**
  143. * Checks if the default ean label is enabled.
  144. *
  145. * @return bool
  146. */
  147. protected function isDefaultEanLabelEnabled() {
  148. $label = $this->getLabel();
  149. $font = $this->font;
  150. return $label !== null && $label !== '' && $font !== null && $this->defaultLabel !== null;
  151. }
  152. /**
  153. * Validates the input.
  154. */
  155. protected function validate() {
  156. $c = strlen($this->text);
  157. if ($c === 0) {
  158. throw new BCGParseException('upce', 'No data has been entered.');
  159. }
  160. // Checking if all chars are allowed
  161. for ($i = 0; $i < $c; $i++) {
  162. if (array_search($this->text[$i], $this->keys) === false) {
  163. throw new BCGParseException('upce', 'The character \'' . $this->text[$i] . '\' is not allowed.');
  164. }
  165. }
  166. // Must contain 11 chars
  167. // Must contain 6 chars (if starting with upce directly)
  168. // First Chars must be 0 or 1
  169. if ($c !== 11 && $c !== 6) {
  170. throw new BCGParseException('upce', 'You must provide a UPC-A (11 characters) or a UPC-E (6 characters).');
  171. } elseif ($this->text[0] !== '0' && $this->text[0] !== '1' && $c !== 6) {
  172. throw new BCGParseException('upce', 'UPC-A must start with 0 or 1 to be converted to UPC-E.');
  173. }
  174. // Convert part
  175. $this->upce = '';
  176. if ($c !== 6) {
  177. // Checking if UPC-A is convertible
  178. $temp1 = substr($this->text, 3, 3);
  179. if ($temp1 === '000' || $temp1 === '100' || $temp1 === '200') { // manufacturer code ends with 100, 200 or 300
  180. if (substr($this->text, 6, 2) === '00') { // Product must start with 00
  181. $this->upce = substr($this->text, 1, 2) . substr($this->text, 8, 3) . substr($this->text, 3, 1);
  182. }
  183. } elseif (substr($this->text, 4, 2) === '00') { // manufacturer code ends with 00
  184. if (substr($this->text, 6, 3) === '000') { // Product must start with 000
  185. $this->upce = substr($this->text, 1, 3) . substr($this->text, 9, 2) . '3';
  186. }
  187. } elseif (substr($this->text, 5, 1) === '0') { // manufacturer code ends with 0
  188. if (substr($this->text, 6, 4) === '0000') { // Product must start with 0000
  189. $this->upce = substr($this->text, 1, 4) . substr($this->text, 10, 1) . '4';
  190. }
  191. } else { // No zero leading at manufacturer code
  192. $temp2 = intval(substr($this->text, 10, 1));
  193. if (substr($this->text, 6, 4) === '0000' && $temp2 >= 5 && $temp2 <= 9) { // Product must start with 0000 and must end by 5, 6, 7, 8 or 9
  194. $this->upce = substr($this->text, 1, 5) . substr($this->text, 10, 1);
  195. }
  196. }
  197. } else {
  198. $this->upce = $this->text;
  199. }
  200. if ($this->upce === '') {
  201. throw new BCGParseException('upce', 'Your UPC-A can\'t be converted to UPC-E.');
  202. }
  203. if ($c === 6) {
  204. $upca = '';
  205. // We convert UPC-E to UPC-A to find the checksum
  206. if ($this->text[5] === '0' || $this->text[5] === '1' || $this->text[5] === '2') {
  207. $upca = substr($this->text, 0, 2) . $this->text[5] . '0000' . substr($this->text, 2, 3);
  208. } elseif ($this->text[5] === '3') {
  209. $upca = substr($this->text, 0, 3) . '00000' . substr($this->text, 3, 2);
  210. } elseif ($this->text[5] === '4') {
  211. $upca = substr($this->text, 0, 4) . '00000' . $this->text[4];
  212. } else {
  213. $upca = substr($this->text, 0, 5) . '0000' . $this->text[5];
  214. }
  215. $this->text = '0' . $upca;
  216. }
  217. parent::validate();
  218. }
  219. /**
  220. * Overloaded method to calculate checksum.
  221. */
  222. protected function calculateChecksum() {
  223. // Calculating Checksum
  224. // Consider the right-most digit of the message to be in an "odd" position,
  225. // and assign odd/even to each character moving from right to left
  226. // Odd Position = 3, Even Position = 1
  227. // Multiply it by the number
  228. // Add all of that and do 10-(?mod10)
  229. $odd = true;
  230. $this->checksumValue = 0;
  231. $c = strlen($this->text);
  232. for ($i = $c; $i > 0; $i--) {
  233. if ($odd === true) {
  234. $multiplier = 3;
  235. $odd = false;
  236. } else {
  237. $multiplier = 1;
  238. $odd = true;
  239. }
  240. if (!isset($this->keys[$this->text[$i - 1]])) {
  241. return;
  242. }
  243. $this->checksumValue += $this->keys[$this->text[$i - 1]] * $multiplier;
  244. }
  245. $this->checksumValue = (10 - $this->checksumValue % 10) % 10;
  246. }
  247. /**
  248. * Overloaded method to display the checksum.
  249. */
  250. protected function processChecksum() {
  251. if ($this->checksumValue === false) { // Calculate the checksum only once
  252. $this->calculateChecksum();
  253. }
  254. if ($this->checksumValue !== false) {
  255. return $this->keys[$this->checksumValue];
  256. }
  257. return false;
  258. }
  259. /**
  260. * Draws the extended bars on the image.
  261. *
  262. * @param resource $im
  263. * @param int $plus
  264. */
  265. protected function drawExtendedBars($im, $plus) {
  266. $rememberX = $this->positionX;
  267. $rememberH = $this->thickness;
  268. // We increase the bars
  269. $this->thickness = $this->thickness + intval($plus / $this->scale);
  270. $this->positionX = 0;
  271. $this->drawSingleBar($im, BCGBarcode::COLOR_FG);
  272. $this->positionX += 2;
  273. $this->drawSingleBar($im, BCGBarcode::COLOR_FG);
  274. // Last Bars
  275. $this->positionX += 46;
  276. $this->drawSingleBar($im, BCGBarcode::COLOR_FG);
  277. $this->positionX += 2;
  278. $this->drawSingleBar($im, BCGBarcode::COLOR_FG);
  279. $this->positionX = $rememberX;
  280. $this->thickness = $rememberH;
  281. }
  282. /**
  283. * Inverses the string when the $inverse parameter is equal to 1.
  284. *
  285. * @param string $text
  286. * @param int $inverse
  287. * @return string
  288. */
  289. private static function inverse($text, $inverse = 1) {
  290. if ($inverse === 1) {
  291. $text = strrev($text);
  292. }
  293. return $text;
  294. }
  295. }
  296. ?>