setQuietZone(true); $this->setThickness(9); } /** * Gets the Quiet zone. * * @return bool */ public function getQuietZone() { return $this->quietZone; } /** * Sets the Quiet zone. * * @param bool $quietZone */ public function setQuietZone($quietZone) { $this->quietZone = (bool)$quietZone; } /** * Sets the tracking code. * * @param int $barcodeIdentifier 2-digit number. 2nd digit must be 0-4 * @param int $serviceTypeIdentifier 3 digits * @param int $mailerIdentifier 6 or 9 digits * @param int $serialNumber 9 (if mailerId is 6) or 6 digits (if mailerId is 9) */ public function setTrackingCode($barcodeIdentifier, $serviceTypeIdentifier, $mailerIdentifier, $serialNumber) { $barcodeIdentifier = (string)(int)$barcodeIdentifier; $serviceTypeIdentifier = (int)$serviceTypeIdentifier; $mailerIdentifier = (int)$mailerIdentifier; $serialNumber = (string)(int)$serialNumber; $barcodeIdentifier = str_pad($barcodeIdentifier, 2, '0', STR_PAD_LEFT); if (strlen($barcodeIdentifier) !== 2) { throw new BCGArgumentException('Barcode Identifier must contain 2 digits.', 'barcodeIdentifier'); } $barcodeIdentifierSecondNumber = $barcodeIdentifier[1]; if ($barcodeIdentifierSecondNumber !== '0' && $barcodeIdentifierSecondNumber !== '1' && $barcodeIdentifierSecondNumber !== '2' && $barcodeIdentifierSecondNumber !== '3' && $barcodeIdentifierSecondNumber !== '4') { throw new BCGArgumentException('Barcode Identifier second digit must be a number between 0 and 4.', 'barcodeIdentifier'); } if ($serviceTypeIdentifier < 0 || $serviceTypeIdentifier > 999) { throw new BCGArgumentException('Service Type Identifier must be between 0 and 999.', 'serviceTypeIdentifier'); } $mailerIdentifierLength = 6; if ($mailerIdentifier > 899999) { $mailerIdentifierLength = 9; } if ($mailerIdentifierLength === 9 && strlen($serialNumber) > 6) { throw new BCGArgumentException('If the Serial Number has more than 6 digits, the Mailer Identifier must be lower than 900000.', 'mailerIdentifier'); } if ($mailerIdentifierLength === 9) { if ($mailerIdentifierLength < 0 || $mailerIdentifier > 999999999) { throw new BCGArgumentException('Mailer Identifier must be between 0 and 999999999.', 'mailerIdentifier'); } } $this->barcodeIdentifier = $barcodeIdentifier; $this->serviceTypeIdentifier = str_pad($serviceTypeIdentifier, 3, '0', STR_PAD_LEFT); $this->mailerIdentifier = str_pad($mailerIdentifier, $mailerIdentifierLength, '0', STR_PAD_LEFT); $this->serialNumber = str_pad((int)$serialNumber, $mailerIdentifierLength === 6 ? 9 : 6, '0', STR_PAD_LEFT); } /** * Parses the text before displaying it. * * @param mixed $text */ public function parse($text) { parent::parse($text); $number = self::executeStep1($this->text, $this->barcodeIdentifier, $this->serviceTypeIdentifier, $this->mailerIdentifier, $this->serialNumber); $crc = self::executeStep2($number); $codewords = self::executeStep3($number); $codewords = self::executeStep4($codewords, $crc); $characters = self::executeStep5($codewords, $crc); $this->data = self::executeStep6($characters); } /** * Draws the barcode. * * @param resource $im */ public function draw($im) { if ($this->quietZone) { $this->positionX += 9; } $c = strlen($this->data); for ($i = 0; $i < $c; $i++) { $this->drawChar($im, $this->data[$i]); } $this->drawText($im, 0, 0, $this->positionX, $this->thickness + ($this->quietZone ? 4 : 0)); } /** * Returns the maximal size of a barcode. * * @param int $w * @param int $h * @return int[] */ public function getDimension($w, $h) { $w += 65 * 3; $h += $this->thickness; // We remove the white on the right $w -= 1.56; if ($this->quietZone) { $w += 18; $h += 4; } return parent::getDimension($w, $h); } /** * Validates the input. */ protected function validate() { // Tracking must have been entered if ($this->barcodeIdentifier === null || $this->serviceTypeIdentifier === null || $this->mailerIdentifier === null || $this->serialNumber === null) { throw new BCGParseException('intelligentmail', 'The tracking code must be set before calling the parse method.'); } // Checking if all chars are allowed $match = array(); if (preg_match('/[^0-9]/', $this->text, $match)) { throw new BCGParseException('intelligentmail', 'The character \'' . $match[0] . '\' is not allowed.'); } // Must contain 0, 5, 9 or 11 chars $c = strlen($this->text); if ($c !== 0 && $c !== 5 && $c !== 9 && $c !== 11) { throw new BCGParseException('intelligentmail', 'Must contain 0, 5, 9, or 11 characters.'); } parent::validate(); } /** * Overloaded method for drawing special barcode. * * @param resource $im * @param string $code * @param boolean $startBar */ protected function drawChar($im, $code, $startBar = true) { $y1 = 0; $y2 = 0; switch ($code) { case 'A': $y1 = 0; $y2 = $this->thickness - ($this->thickness / 2.5); break; case 'D': $y1 = 3.096; $y2 = $this->thickness - 1; break; case 'F': $y1 = 0; $y2 = $this->thickness - 1; break; case 'T': $y1 = 3.096; $y2 = $this->thickness - ($this->thickness / 2.5); break; } if ($this->quietZone) { $y1 += 2; $y2 += 2; } $this->drawFilledRectangle($im, $this->positionX, $y1, $this->positionX + 0.44, $y2, BCGBarcode::COLOR_FG); $this->positionX += 3; } /** * Executes Step 1: Conversion of Data Fields into Binary Data * * @param string $text * @param string $barcodeIdentifier * @param string $serviceTypeIdentifier * @param string $mailerIdentifier * @param string $serialNumber * @return string BCNumber */ private static function executeStep1($text, $barcodeIdentifier, $serviceTypeIdentifier, $mailerIdentifier, $serialNumber) { $number = self::conversionRoutingCode($text); $number = self::conversionTrackingCode($number, $barcodeIdentifier, $serviceTypeIdentifier, $mailerIdentifier, $serialNumber); return $number; } /** * Executes Step 2: Generation of 11-Bit CRC on Binary Data * * @param $number BCNumber * @return int */ private static function executeStep2($number) { $byteArray = str_pad(self::bcdecuc($number), 13, chr(0), STR_PAD_LEFT); $generatorPolynomial = 0x0f35; $frameCheckSequence = 0x07ff; $data = 0; $byteIndex = 0; $bit = 0; $data = (ord($byteArray[$byteIndex]) << 5) & 0xffff; for ($bit = 2; $bit < 8; $bit++) { if (($frameCheckSequence ^ $data) & 0x400) { $frameCheckSequence = ($frameCheckSequence << 1) ^ $generatorPolynomial; } else { $frameCheckSequence = ($frameCheckSequence << 1); } $frameCheckSequence &= 0x7ff; $data <<= 1; $data &= 0xffff; } for ($byteIndex = 1; $byteIndex < 13; $byteIndex++) { $data = (ord($byteArray[$byteIndex]) << 3) & 0xffff; for ($bit = 0; $bit < 8; $bit++) { if (($frameCheckSequence ^ $data) & 0x0400) { $frameCheckSequence = ($frameCheckSequence << 1) ^ $generatorPolynomial; } else { $frameCheckSequence = ($frameCheckSequence << 1); } $frameCheckSequence &= 0x7ff; $data <<= 1; $data &= 0xffff; } } return $frameCheckSequence; } /** * Executes Step 3: Conversion from Binary Data to Codewords * * @param string $number BCNumber * @return int[] */ private static function executeStep3($number) { $codewords = array(); $codewords[9] = (int)bcmod($number, '636'); $number = bcdiv($number, '636', 0); for ($i = 8; $i >= 0; $i--) { $codewords[$i] = (int)bcmod($number, '1365'); $number = bcdiv($number, '1365', 0); } return $codewords; } /** * Executes Step 4: Inserting Additional Information into Codewords * * @param int[] $codewords * @param int $crc * @return int[] */ private static function executeStep4($codewords, $crc) { $codewords[9] *= 2; if ($crc & 0x400) { $codewords[0] += 659; } return $codewords; } /** * Executes Step 5: Conversion from Codewords to Characters * * @param int[] $codewords * @param int $crc * @return int[] */ private static function executeStep5($codewords, $crc) { $characters = array(); for ($i = 0; $i < 10; $i++) { if ($codewords[$i] <= 1286) { $characters[$i] = self::$characterTable1[$codewords[$i]]; } else { $characters[$i] = self::$characterTable2[$codewords[$i] - 1287]; } } for ($i = 0; $i < 10; $i++) { $mask = 1 << $i; if ($crc & $mask) { $characters[$i] ^= 0x1fff; } } return $characters; } /** * Executes Step 6: Conversion from Characters to the Intelligent Mail Barcode * * @param int[] $characters * @return string */ private static function executeStep6($characters) { $bars = ''; for ($i = 0; $i < 65; $i++) { $barPosition = self::$barPositions[$i]; $descender = $barPosition[0]; $ascender = $barPosition[1]; $extenderDescender = !!($characters[$descender[0]] & (1 << $descender[1])); $extenderAscender = !!($characters[$ascender[0]] & (1 << $ascender[1])); if ($extenderDescender && $extenderAscender) { $bars .= 'F'; } elseif ($extenderDescender) { $bars .= 'D'; } elseif ($extenderAscender) { $bars .= 'A'; } else { $bars .= 'T'; } } return $bars; } /** * Converts the routing code zipcode. * * @param string $zipcode * @return string BCNumber */ private static function conversionRoutingCode($zipcode) { $number = $zipcode; switch (strlen($zipcode)) { case 11: $number = bcadd($number, '1000000000', 0); case 9: $number = bcadd($number, '100000', 0); case 5: $number = bcadd($number, '1', 0); default: return $number; } } /** * Converts the tracking code number. * * @param string $number BCNumber * @param string $barcodeIdentifier * @param string $serviceTypeIdentifier * @param string $mailerIdentifier * @param string $serialNumber * @return string BCNumber */ private static function conversionTrackingCode($number, $barcodeIdentifier, $serviceTypeIdentifier, $mailerIdentifier, $serialNumber) { $number = bcmul($number, 10, 0); $number = bcadd($number, $barcodeIdentifier[0], 0); $number = bcmul($number, 5, 0); $number = bcadd($number, $barcodeIdentifier[1], 0); $temp = $serviceTypeIdentifier . $mailerIdentifier . $serialNumber; for ($i = 0; $i < 18; $i++) { $number = bcmul($number, 10, 0); $number = bcadd($number, $temp[$i], 0); } return $number; } /** * Transforms a BCNumber into unsigned char*. * * @param string $dec BCNumber * @param string */ private static function bcdecuc($dec) { $last = bcmod($dec, 256); $remain = bcdiv(bcsub($dec, $last), 256, 0); if ($remain == 0) { return pack('C', $last); } else { return self::bcdecuc($remain) . pack('C', $last); } } } ?>