作者 开飞机的舒克

后台修改

要显示太多修改。

为保证性能只显示 28 of 28+ 个文件。

  1 +{"files":[],"license":"regular","licenseto":"10789","licensekey":"ALX6p58UdrSQ2PB4 BKjAXAL2Qq8\/J3UeiZyXfQ==","domains":["campus.cn"],"licensecodes":[],"validations":["c51a74160ee0aa6a19729b33d4a9fcdc"]}
  1 +<?php
  2 +
  3 +namespace addons\barcode;
  4 +
  5 +use think\Addons;
  6 +
  7 +/**
  8 + * 条码生成
  9 + */
  10 +class Barcode extends Addons {
  11 +
  12 + /**
  13 + * 插件安装方法
  14 + * @return bool
  15 + */
  16 + public function install() {
  17 + return true;
  18 + }
  19 +
  20 + /**
  21 + * 插件卸载方法
  22 + * @return bool
  23 + */
  24 + public function uninstall() {
  25 + return true;
  26 + }
  27 +
  28 + /**
  29 + * 插件启用方法
  30 + * @return bool
  31 + */
  32 + public function enable() {
  33 + return true;
  34 + }
  35 +
  36 + /**
  37 + * 插件禁用方法
  38 + * @return bool
  39 + */
  40 + public function disable() {
  41 + return true;
  42 + }
  43 +
  44 + /**
  45 + * 应用初始化
  46 + */
  47 + public function appInit()
  48 + {
  49 + if(!class_exists("\Picqer\Barcode")){
  50 + \think\Loader::addNamespace('Picqer\Barcode', ADDON_PATH . 'barcode' . DS . 'library' . DS . 'Barcode' . DS);
  51 + }
  52 + }
  53 +}
  1 +<?php
  2 +
  3 +return [
  4 + [
  5 + 'name' => 'rewrite',
  6 + 'title' => '伪静态',
  7 + 'type' => 'array',
  8 + 'content' => [],
  9 + 'value' => [
  10 + 'study/add' => '/qrcode$',
  11 + ],
  12 + 'rule' => 'required',
  13 + 'msg' => '',
  14 + 'tip' => '',
  15 + 'ok' => '',
  16 + 'extend' => '',
  17 + ],
  18 +];
  1 +<?php
  2 +
  3 +namespace addons\barcode\controller;
  4 +
  5 +use think\addons\Controller;
  6 +use think\Response;
  7 +
  8 +/**
  9 + * 条码生成
  10 + * @package addons\barcode\controller
  11 + */
  12 +class Index extends Controller {
  13 +
  14 + public function index() {
  15 + return $this->view->fetch();
  16 + }
  17 +
  18 + // 生成条码
  19 + public function build() {
  20 + $text = $this->request->get('text', '1234567890');
  21 + $type = $this->request->get('type', 'C128');
  22 + $foreground = $this->request->get('foreground', "#000000");
  23 + $width = $this->request->get('width', 2);
  24 + $height = $this->request->get('height', 30);
  25 +
  26 + $params = [
  27 + 'text' => $text,
  28 + 'type' => $type,
  29 + 'foreground' => $foreground,
  30 + 'width' => $width,
  31 + 'height' => $height,
  32 + ];
  33 +
  34 + $barcode = \addons\barcode\library\Service::barcode($params);
  35 +
  36 + // 直接显示条码
  37 + $response = Response::create()->header("Content-Type", "image/png");
  38 + header('Content-Type: image/png');
  39 + $response->content($barcode);
  40 + return $response;
  41 + }
  42 +
  43 +}
  1 +name = barcode
  2 +title = 条码生成
  3 +intro = 生成条码示例插件
  4 +author = aa820t
  5 +website = https://ask.fastadmin.net/u/26861
  6 +version = 1.0.0
  7 +state = 1
  8 +license = regular
  9 +licenseto = 10789
  10 +url = /addons/barcode
  1 +<?php
  2 +
  3 +/**
  4 + * General PHP Barcode Generator
  5 + *
  6 + * @author Casper Bakker - picqer.com
  7 + * Based on TCPDF Barcode Generator
  8 + */
  9 +
  10 +// Copyright (C) 2002-2015 Nicola Asuni - Tecnick.com LTD
  11 +//
  12 +// This file is part of TCPDF software library.
  13 +//
  14 +// TCPDF is free software: you can redistribute it and/or modify it
  15 +// under the terms of the GNU Lesser General Public License as
  16 +// published by the Free Software Foundation, either version 3 of the
  17 +// License, or (at your option) any later version.
  18 +//
  19 +// TCPDF is distributed in the hope that it will be useful, but
  20 +// WITHOUT ANY WARRANTY; without even the implied warranty of
  21 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  22 +// See the GNU Lesser General Public License for more details.
  23 +//
  24 +// You should have received a copy of the License
  25 +// along with TCPDF. If not, see
  26 +// <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
  27 +//
  28 +// See LICENSE.TXT file for more information.
  29 +
  30 +namespace Picqer\Barcode;
  31 +
  32 +use Picqer\Barcode\Exceptions\BarcodeException;
  33 +use Picqer\Barcode\Exceptions\InvalidCharacterException;
  34 +use Picqer\Barcode\Exceptions\InvalidCheckDigitException;
  35 +use Picqer\Barcode\Exceptions\InvalidFormatException;
  36 +use Picqer\Barcode\Exceptions\InvalidLengthException;
  37 +use Picqer\Barcode\Exceptions\UnknownTypeException;
  38 +
  39 +abstract class BarcodeGenerator
  40 +{
  41 + const TYPE_CODE_39 = 'C39';
  42 + const TYPE_CODE_39_CHECKSUM = 'C39+';
  43 + const TYPE_CODE_39E = 'C39E';
  44 + const TYPE_CODE_39E_CHECKSUM = 'C39E+';
  45 + const TYPE_CODE_93 = 'C93';
  46 + const TYPE_STANDARD_2_5 = 'S25';
  47 + const TYPE_STANDARD_2_5_CHECKSUM = 'S25+';
  48 + const TYPE_INTERLEAVED_2_5 = 'I25';
  49 + const TYPE_INTERLEAVED_2_5_CHECKSUM = 'I25+';
  50 + const TYPE_CODE_128 = 'C128';
  51 + const TYPE_CODE_128_A = 'C128A';
  52 + const TYPE_CODE_128_B = 'C128B';
  53 + const TYPE_CODE_128_C = 'C128C';
  54 + const TYPE_EAN_2 = 'EAN2';
  55 + const TYPE_EAN_5 = 'EAN5';
  56 + const TYPE_EAN_8 = 'EAN8';
  57 + const TYPE_EAN_13 = 'EAN13';
  58 + const TYPE_UPC_A = 'UPCA';
  59 + const TYPE_UPC_E = 'UPCE';
  60 + const TYPE_MSI = 'MSI';
  61 + const TYPE_MSI_CHECKSUM = 'MSI+';
  62 + const TYPE_POSTNET = 'POSTNET';
  63 + const TYPE_PLANET = 'PLANET';
  64 + const TYPE_RMS4CC = 'RMS4CC';
  65 + const TYPE_KIX = 'KIX';
  66 + const TYPE_IMB = 'IMB';
  67 + const TYPE_CODABAR = 'CODABAR';
  68 + const TYPE_CODE_11 = 'CODE11';
  69 + const TYPE_PHARMA_CODE = 'PHARMA';
  70 + const TYPE_PHARMA_CODE_TWO_TRACKS = 'PHARMA2T';
  71 +
  72 + /**
  73 + * Get the barcode data
  74 + *
  75 + * @param string $code code to print
  76 + * @param string $type type of barcode
  77 + * @return array barcode array
  78 + * @public
  79 + */
  80 + protected function getBarcodeData($code, $type)
  81 + {
  82 + switch (strtoupper($type)) {
  83 + case self::TYPE_CODE_39: { // CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
  84 + $arrcode = $this->barcode_code39($code, false, false);
  85 + break;
  86 + }
  87 + case self::TYPE_CODE_39_CHECKSUM: { // CODE 39 with checksum
  88 + $arrcode = $this->barcode_code39($code, false, true);
  89 + break;
  90 + }
  91 + case self::TYPE_CODE_39E: { // CODE 39 EXTENDED
  92 + $arrcode = $this->barcode_code39($code, true, false);
  93 + break;
  94 + }
  95 + case self::TYPE_CODE_39E_CHECKSUM: { // CODE 39 EXTENDED + CHECKSUM
  96 + $arrcode = $this->barcode_code39($code, true, true);
  97 + break;
  98 + }
  99 + case self::TYPE_CODE_93: { // CODE 93 - USS-93
  100 + $arrcode = $this->barcode_code93($code);
  101 + break;
  102 + }
  103 + case self::TYPE_STANDARD_2_5: { // Standard 2 of 5
  104 + $arrcode = $this->barcode_s25($code, false);
  105 + break;
  106 + }
  107 + case self::TYPE_STANDARD_2_5_CHECKSUM: { // Standard 2 of 5 + CHECKSUM
  108 + $arrcode = $this->barcode_s25($code, true);
  109 + break;
  110 + }
  111 + case self::TYPE_INTERLEAVED_2_5: { // Interleaved 2 of 5
  112 + $arrcode = $this->barcode_i25($code, false);
  113 + break;
  114 + }
  115 + case self::TYPE_INTERLEAVED_2_5_CHECKSUM: { // Interleaved 2 of 5 + CHECKSUM
  116 + $arrcode = $this->barcode_i25($code, true);
  117 + break;
  118 + }
  119 + case self::TYPE_CODE_128: { // CODE 128
  120 + $arrcode = $this->barcode_c128($code, '');
  121 + break;
  122 + }
  123 + case self::TYPE_CODE_128_A: { // CODE 128 A
  124 + $arrcode = $this->barcode_c128($code, 'A');
  125 + break;
  126 + }
  127 + case self::TYPE_CODE_128_B: { // CODE 128 B
  128 + $arrcode = $this->barcode_c128($code, 'B');
  129 + break;
  130 + }
  131 + case self::TYPE_CODE_128_C: { // CODE 128 C
  132 + $arrcode = $this->barcode_c128($code, 'C');
  133 + break;
  134 + }
  135 + case self::TYPE_EAN_2: { // 2-Digits UPC-Based Extention
  136 + $arrcode = $this->barcode_eanext($code, 2);
  137 + break;
  138 + }
  139 + case self::TYPE_EAN_5: { // 5-Digits UPC-Based Extention
  140 + $arrcode = $this->barcode_eanext($code, 5);
  141 + break;
  142 + }
  143 + case self::TYPE_EAN_8: { // EAN 8
  144 + $arrcode = $this->barcode_eanupc($code, 8);
  145 + break;
  146 + }
  147 + case self::TYPE_EAN_13: { // EAN 13
  148 + $arrcode = $this->barcode_eanupc($code, 13);
  149 + break;
  150 + }
  151 + case self::TYPE_UPC_A: { // UPC-A
  152 + $arrcode = $this->barcode_eanupc($code, 12);
  153 + break;
  154 + }
  155 + case self::TYPE_UPC_E: { // UPC-E
  156 + $arrcode = $this->barcode_eanupc($code, 6);
  157 + break;
  158 + }
  159 + case self::TYPE_MSI: { // MSI (Variation of Plessey code)
  160 + $arrcode = $this->barcode_msi($code, false);
  161 + break;
  162 + }
  163 + case self::TYPE_MSI_CHECKSUM: { // MSI + CHECKSUM (modulo 11)
  164 + $arrcode = $this->barcode_msi($code, true);
  165 + break;
  166 + }
  167 + case self::TYPE_POSTNET: { // POSTNET
  168 + $arrcode = $this->barcode_postnet($code, false);
  169 + break;
  170 + }
  171 + case self::TYPE_PLANET: { // PLANET
  172 + $arrcode = $this->barcode_postnet($code, true);
  173 + break;
  174 + }
  175 + case self::TYPE_RMS4CC: { // RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
  176 + $arrcode = $this->barcode_rms4cc($code, false);
  177 + break;
  178 + }
  179 + case self::TYPE_KIX: { // KIX (Klant index - Customer index)
  180 + $arrcode = $this->barcode_rms4cc($code, true);
  181 + break;
  182 + }
  183 + case self::TYPE_IMB: { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
  184 + $arrcode = $this->barcode_imb($code);
  185 + break;
  186 + }
  187 + case self::TYPE_CODABAR: { // CODABAR
  188 + $arrcode = $this->barcode_codabar($code);
  189 + break;
  190 + }
  191 + case self::TYPE_CODE_11: { // CODE 11
  192 + $arrcode = $this->barcode_code11($code);
  193 + break;
  194 + }
  195 + case self::TYPE_PHARMA_CODE: { // PHARMACODE
  196 + $arrcode = $this->barcode_pharmacode($code);
  197 + break;
  198 + }
  199 + case self::TYPE_PHARMA_CODE_TWO_TRACKS: { // PHARMACODE TWO-TRACKS
  200 + $arrcode = $this->barcode_pharmacode2t($code);
  201 + break;
  202 + }
  203 + default: {
  204 + throw new UnknownTypeException();
  205 + break;
  206 + }
  207 + }
  208 +
  209 + if ( ! isset($arrcode['maxWidth'])) {
  210 + $arrcode = $this->convertBarcodeArrayToNewStyle($arrcode);
  211 + }
  212 +
  213 + return $arrcode;
  214 + }
  215 +
  216 + /**
  217 + * CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
  218 + * General-purpose code in very wide use world-wide
  219 + *
  220 + * @param $code (string) code to represent.
  221 + * @param $extended (boolean) if true uses the extended mode.
  222 + * @param $checksum (boolean) if true add a checksum to the code.
  223 + * @return array barcode representation.
  224 + * @protected
  225 + */
  226 + protected function barcode_code39($code, $extended = false, $checksum = false)
  227 + {
  228 + $chr = [];
  229 + $chr['0'] = '111331311';
  230 + $chr['1'] = '311311113';
  231 + $chr['2'] = '113311113';
  232 + $chr['3'] = '313311111';
  233 + $chr['4'] = '111331113';
  234 + $chr['5'] = '311331111';
  235 + $chr['6'] = '113331111';
  236 + $chr['7'] = '111311313';
  237 + $chr['8'] = '311311311';
  238 + $chr['9'] = '113311311';
  239 + $chr['A'] = '311113113';
  240 + $chr['B'] = '113113113';
  241 + $chr['C'] = '313113111';
  242 + $chr['D'] = '111133113';
  243 + $chr['E'] = '311133111';
  244 + $chr['F'] = '113133111';
  245 + $chr['G'] = '111113313';
  246 + $chr['H'] = '311113311';
  247 + $chr['I'] = '113113311';
  248 + $chr['J'] = '111133311';
  249 + $chr['K'] = '311111133';
  250 + $chr['L'] = '113111133';
  251 + $chr['M'] = '313111131';
  252 + $chr['N'] = '111131133';
  253 + $chr['O'] = '311131131';
  254 + $chr['P'] = '113131131';
  255 + $chr['Q'] = '111111333';
  256 + $chr['R'] = '311111331';
  257 + $chr['S'] = '113111331';
  258 + $chr['T'] = '111131331';
  259 + $chr['U'] = '331111113';
  260 + $chr['V'] = '133111113';
  261 + $chr['W'] = '333111111';
  262 + $chr['X'] = '131131113';
  263 + $chr['Y'] = '331131111';
  264 + $chr['Z'] = '133131111';
  265 + $chr['-'] = '131111313';
  266 + $chr['.'] = '331111311';
  267 + $chr[' '] = '133111311';
  268 + $chr['$'] = '131313111';
  269 + $chr['/'] = '131311131';
  270 + $chr['+'] = '131113131';
  271 + $chr['%'] = '111313131';
  272 + $chr['*'] = '131131311';
  273 +
  274 + $code = strtoupper($code);
  275 +
  276 + if ($extended) {
  277 + // extended mode
  278 + $code = $this->encode_code39_ext($code);
  279 + }
  280 +
  281 + if ($checksum) {
  282 + // checksum
  283 + $code .= $this->checksum_code39($code);
  284 + }
  285 +
  286 + // add start and stop codes
  287 + $code = '*' . $code . '*';
  288 +
  289 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  290 + $k = 0;
  291 + $clen = strlen($code);
  292 + for ($i = 0; $i < $clen; ++$i) {
  293 + $char = $code[$i];
  294 + if ( ! isset($chr[$char])) {
  295 + throw new InvalidCharacterException('Char ' . $char . ' is unsupported');
  296 + }
  297 + for ($j = 0; $j < 9; ++$j) {
  298 + if (($j % 2) == 0) {
  299 + $t = true; // bar
  300 + } else {
  301 + $t = false; // space
  302 + }
  303 + $w = $chr[$char][$j];
  304 + $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  305 + $bararray['maxw'] += $w;
  306 + ++$k;
  307 + }
  308 + // intercharacter gap
  309 + $bararray['bcode'][$k] = array('t' => false, 'w' => 1, 'h' => 1, 'p' => 0);
  310 + $bararray['maxw'] += 1;
  311 + ++$k;
  312 + }
  313 +
  314 + return $bararray;
  315 + }
  316 +
  317 + /**
  318 + * Encode a string to be used for CODE 39 Extended mode.
  319 + *
  320 + * @param string $code code to represent.
  321 + * @return bool|string encoded string.
  322 + * @protected
  323 + */
  324 + protected function encode_code39_ext($code)
  325 + {
  326 + $encode = array(
  327 + chr(0) => '%U',
  328 + chr(1) => '$A',
  329 + chr(2) => '$B',
  330 + chr(3) => '$C',
  331 + chr(4) => '$D',
  332 + chr(5) => '$E',
  333 + chr(6) => '$F',
  334 + chr(7) => '$G',
  335 + chr(8) => '$H',
  336 + chr(9) => '$I',
  337 + chr(10) => '$J',
  338 + chr(11) => '£K',
  339 + chr(12) => '$L',
  340 + chr(13) => '$M',
  341 + chr(14) => '$N',
  342 + chr(15) => '$O',
  343 + chr(16) => '$P',
  344 + chr(17) => '$Q',
  345 + chr(18) => '$R',
  346 + chr(19) => '$S',
  347 + chr(20) => '$T',
  348 + chr(21) => '$U',
  349 + chr(22) => '$V',
  350 + chr(23) => '$W',
  351 + chr(24) => '$X',
  352 + chr(25) => '$Y',
  353 + chr(26) => '$Z',
  354 + chr(27) => '%A',
  355 + chr(28) => '%B',
  356 + chr(29) => '%C',
  357 + chr(30) => '%D',
  358 + chr(31) => '%E',
  359 + chr(32) => ' ',
  360 + chr(33) => '/A',
  361 + chr(34) => '/B',
  362 + chr(35) => '/C',
  363 + chr(36) => '/D',
  364 + chr(37) => '/E',
  365 + chr(38) => '/F',
  366 + chr(39) => '/G',
  367 + chr(40) => '/H',
  368 + chr(41) => '/I',
  369 + chr(42) => '/J',
  370 + chr(43) => '/K',
  371 + chr(44) => '/L',
  372 + chr(45) => '-',
  373 + chr(46) => '.',
  374 + chr(47) => '/O',
  375 + chr(48) => '0',
  376 + chr(49) => '1',
  377 + chr(50) => '2',
  378 + chr(51) => '3',
  379 + chr(52) => '4',
  380 + chr(53) => '5',
  381 + chr(54) => '6',
  382 + chr(55) => '7',
  383 + chr(56) => '8',
  384 + chr(57) => '9',
  385 + chr(58) => '/Z',
  386 + chr(59) => '%F',
  387 + chr(60) => '%G',
  388 + chr(61) => '%H',
  389 + chr(62) => '%I',
  390 + chr(63) => '%J',
  391 + chr(64) => '%V',
  392 + chr(65) => 'A',
  393 + chr(66) => 'B',
  394 + chr(67) => 'C',
  395 + chr(68) => 'D',
  396 + chr(69) => 'E',
  397 + chr(70) => 'F',
  398 + chr(71) => 'G',
  399 + chr(72) => 'H',
  400 + chr(73) => 'I',
  401 + chr(74) => 'J',
  402 + chr(75) => 'K',
  403 + chr(76) => 'L',
  404 + chr(77) => 'M',
  405 + chr(78) => 'N',
  406 + chr(79) => 'O',
  407 + chr(80) => 'P',
  408 + chr(81) => 'Q',
  409 + chr(82) => 'R',
  410 + chr(83) => 'S',
  411 + chr(84) => 'T',
  412 + chr(85) => 'U',
  413 + chr(86) => 'V',
  414 + chr(87) => 'W',
  415 + chr(88) => 'X',
  416 + chr(89) => 'Y',
  417 + chr(90) => 'Z',
  418 + chr(91) => '%K',
  419 + chr(92) => '%L',
  420 + chr(93) => '%M',
  421 + chr(94) => '%N',
  422 + chr(95) => '%O',
  423 + chr(96) => '%W',
  424 + chr(97) => '+A',
  425 + chr(98) => '+B',
  426 + chr(99) => '+C',
  427 + chr(100) => '+D',
  428 + chr(101) => '+E',
  429 + chr(102) => '+F',
  430 + chr(103) => '+G',
  431 + chr(104) => '+H',
  432 + chr(105) => '+I',
  433 + chr(106) => '+J',
  434 + chr(107) => '+K',
  435 + chr(108) => '+L',
  436 + chr(109) => '+M',
  437 + chr(110) => '+N',
  438 + chr(111) => '+O',
  439 + chr(112) => '+P',
  440 + chr(113) => '+Q',
  441 + chr(114) => '+R',
  442 + chr(115) => '+S',
  443 + chr(116) => '+T',
  444 + chr(117) => '+U',
  445 + chr(118) => '+V',
  446 + chr(119) => '+W',
  447 + chr(120) => '+X',
  448 + chr(121) => '+Y',
  449 + chr(122) => '+Z',
  450 + chr(123) => '%P',
  451 + chr(124) => '%Q',
  452 + chr(125) => '%R',
  453 + chr(126) => '%S',
  454 + chr(127) => '%T'
  455 + );
  456 + $code_ext = '';
  457 + $clen = strlen($code);
  458 + for ($i = 0; $i < $clen; ++$i) {
  459 + if (ord($code[$i]) > 127) {
  460 + throw new InvalidCharacterException('Only supports till char 127');
  461 + }
  462 + $code_ext .= $encode[$code[$i]];
  463 + }
  464 +
  465 + return $code_ext;
  466 + }
  467 +
  468 + /**
  469 + * Calculate CODE 39 checksum (modulo 43).
  470 + *
  471 + * @param string $code code to represent.
  472 + * @return string char checksum.
  473 + * @protected
  474 + */
  475 + protected function checksum_code39($code)
  476 + {
  477 + $chars = array(
  478 + '0',
  479 + '1',
  480 + '2',
  481 + '3',
  482 + '4',
  483 + '5',
  484 + '6',
  485 + '7',
  486 + '8',
  487 + '9',
  488 + 'A',
  489 + 'B',
  490 + 'C',
  491 + 'D',
  492 + 'E',
  493 + 'F',
  494 + 'G',
  495 + 'H',
  496 + 'I',
  497 + 'J',
  498 + 'K',
  499 + 'L',
  500 + 'M',
  501 + 'N',
  502 + 'O',
  503 + 'P',
  504 + 'Q',
  505 + 'R',
  506 + 'S',
  507 + 'T',
  508 + 'U',
  509 + 'V',
  510 + 'W',
  511 + 'X',
  512 + 'Y',
  513 + 'Z',
  514 + '-',
  515 + '.',
  516 + ' ',
  517 + '$',
  518 + '/',
  519 + '+',
  520 + '%'
  521 + );
  522 + $sum = 0;
  523 + $codelength = strlen($code);
  524 + for ($i = 0; $i < $codelength; ++$i) {
  525 + $k = array_keys($chars, $code[$i]);
  526 + $sum += $k[0];
  527 + }
  528 + $j = ($sum % 43);
  529 +
  530 + return $chars[$j];
  531 + }
  532 +
  533 + /**
  534 + * CODE 93 - USS-93
  535 + * Compact code similar to Code 39
  536 + *
  537 + * @param $code (string) code to represent.
  538 + * @return array barcode representation.
  539 + * @protected
  540 + */
  541 + protected function barcode_code93($code)
  542 + {
  543 + $chr = [];
  544 + $chr[48] = '131112'; // 0
  545 + $chr[49] = '111213'; // 1
  546 + $chr[50] = '111312'; // 2
  547 + $chr[51] = '111411'; // 3
  548 + $chr[52] = '121113'; // 4
  549 + $chr[53] = '121212'; // 5
  550 + $chr[54] = '121311'; // 6
  551 + $chr[55] = '111114'; // 7
  552 + $chr[56] = '131211'; // 8
  553 + $chr[57] = '141111'; // 9
  554 + $chr[65] = '211113'; // A
  555 + $chr[66] = '211212'; // B
  556 + $chr[67] = '211311'; // C
  557 + $chr[68] = '221112'; // D
  558 + $chr[69] = '221211'; // E
  559 + $chr[70] = '231111'; // F
  560 + $chr[71] = '112113'; // G
  561 + $chr[72] = '112212'; // H
  562 + $chr[73] = '112311'; // I
  563 + $chr[74] = '122112'; // J
  564 + $chr[75] = '132111'; // K
  565 + $chr[76] = '111123'; // L
  566 + $chr[77] = '111222'; // M
  567 + $chr[78] = '111321'; // N
  568 + $chr[79] = '121122'; // O
  569 + $chr[80] = '131121'; // P
  570 + $chr[81] = '212112'; // Q
  571 + $chr[82] = '212211'; // R
  572 + $chr[83] = '211122'; // S
  573 + $chr[84] = '211221'; // T
  574 + $chr[85] = '221121'; // U
  575 + $chr[86] = '222111'; // V
  576 + $chr[87] = '112122'; // W
  577 + $chr[88] = '112221'; // X
  578 + $chr[89] = '122121'; // Y
  579 + $chr[90] = '123111'; // Z
  580 + $chr[45] = '121131'; // -
  581 + $chr[46] = '311112'; // .
  582 + $chr[32] = '311211'; //
  583 + $chr[36] = '321111'; // $
  584 + $chr[47] = '112131'; // /
  585 + $chr[43] = '113121'; // +
  586 + $chr[37] = '211131'; // %
  587 + $chr[128] = '121221'; // ($)
  588 + $chr[129] = '311121'; // (/)
  589 + $chr[130] = '122211'; // (+)
  590 + $chr[131] = '312111'; // (%)
  591 + $chr[42] = '111141'; // start-stop
  592 + $code = strtoupper($code);
  593 + $encode = array(
  594 + chr(0) => chr(131) . 'U',
  595 + chr(1) => chr(128) . 'A',
  596 + chr(2) => chr(128) . 'B',
  597 + chr(3) => chr(128) . 'C',
  598 + chr(4) => chr(128) . 'D',
  599 + chr(5) => chr(128) . 'E',
  600 + chr(6) => chr(128) . 'F',
  601 + chr(7) => chr(128) . 'G',
  602 + chr(8) => chr(128) . 'H',
  603 + chr(9) => chr(128) . 'I',
  604 + chr(10) => chr(128) . 'J',
  605 + chr(11) => '£K',
  606 + chr(12) => chr(128) . 'L',
  607 + chr(13) => chr(128) . 'M',
  608 + chr(14) => chr(128) . 'N',
  609 + chr(15) => chr(128) . 'O',
  610 + chr(16) => chr(128) . 'P',
  611 + chr(17) => chr(128) . 'Q',
  612 + chr(18) => chr(128) . 'R',
  613 + chr(19) => chr(128) . 'S',
  614 + chr(20) => chr(128) . 'T',
  615 + chr(21) => chr(128) . 'U',
  616 + chr(22) => chr(128) . 'V',
  617 + chr(23) => chr(128) . 'W',
  618 + chr(24) => chr(128) . 'X',
  619 + chr(25) => chr(128) . 'Y',
  620 + chr(26) => chr(128) . 'Z',
  621 + chr(27) => chr(131) . 'A',
  622 + chr(28) => chr(131) . 'B',
  623 + chr(29) => chr(131) . 'C',
  624 + chr(30) => chr(131) . 'D',
  625 + chr(31) => chr(131) . 'E',
  626 + chr(32) => ' ',
  627 + chr(33) => chr(129) . 'A',
  628 + chr(34) => chr(129) . 'B',
  629 + chr(35) => chr(129) . 'C',
  630 + chr(36) => chr(129) . 'D',
  631 + chr(37) => chr(129) . 'E',
  632 + chr(38) => chr(129) . 'F',
  633 + chr(39) => chr(129) . 'G',
  634 + chr(40) => chr(129) . 'H',
  635 + chr(41) => chr(129) . 'I',
  636 + chr(42) => chr(129) . 'J',
  637 + chr(43) => chr(129) . 'K',
  638 + chr(44) => chr(129) . 'L',
  639 + chr(45) => '-',
  640 + chr(46) => '.',
  641 + chr(47) => chr(129) . 'O',
  642 + chr(48) => '0',
  643 + chr(49) => '1',
  644 + chr(50) => '2',
  645 + chr(51) => '3',
  646 + chr(52) => '4',
  647 + chr(53) => '5',
  648 + chr(54) => '6',
  649 + chr(55) => '7',
  650 + chr(56) => '8',
  651 + chr(57) => '9',
  652 + chr(58) => chr(129) . 'Z',
  653 + chr(59) => chr(131) . 'F',
  654 + chr(60) => chr(131) . 'G',
  655 + chr(61) => chr(131) . 'H',
  656 + chr(62) => chr(131) . 'I',
  657 + chr(63) => chr(131) . 'J',
  658 + chr(64) => chr(131) . 'V',
  659 + chr(65) => 'A',
  660 + chr(66) => 'B',
  661 + chr(67) => 'C',
  662 + chr(68) => 'D',
  663 + chr(69) => 'E',
  664 + chr(70) => 'F',
  665 + chr(71) => 'G',
  666 + chr(72) => 'H',
  667 + chr(73) => 'I',
  668 + chr(74) => 'J',
  669 + chr(75) => 'K',
  670 + chr(76) => 'L',
  671 + chr(77) => 'M',
  672 + chr(78) => 'N',
  673 + chr(79) => 'O',
  674 + chr(80) => 'P',
  675 + chr(81) => 'Q',
  676 + chr(82) => 'R',
  677 + chr(83) => 'S',
  678 + chr(84) => 'T',
  679 + chr(85) => 'U',
  680 + chr(86) => 'V',
  681 + chr(87) => 'W',
  682 + chr(88) => 'X',
  683 + chr(89) => 'Y',
  684 + chr(90) => 'Z',
  685 + chr(91) => chr(131) . 'K',
  686 + chr(92) => chr(131) . 'L',
  687 + chr(93) => chr(131) . 'M',
  688 + chr(94) => chr(131) . 'N',
  689 + chr(95) => chr(131) . 'O',
  690 + chr(96) => chr(131) . 'W',
  691 + chr(97) => chr(130) . 'A',
  692 + chr(98) => chr(130) . 'B',
  693 + chr(99) => chr(130) . 'C',
  694 + chr(100) => chr(130) . 'D',
  695 + chr(101) => chr(130) . 'E',
  696 + chr(102) => chr(130) . 'F',
  697 + chr(103) => chr(130) . 'G',
  698 + chr(104) => chr(130) . 'H',
  699 + chr(105) => chr(130) . 'I',
  700 + chr(106) => chr(130) . 'J',
  701 + chr(107) => chr(130) . 'K',
  702 + chr(108) => chr(130) . 'L',
  703 + chr(109) => chr(130) . 'M',
  704 + chr(110) => chr(130) . 'N',
  705 + chr(111) => chr(130) . 'O',
  706 + chr(112) => chr(130) . 'P',
  707 + chr(113) => chr(130) . 'Q',
  708 + chr(114) => chr(130) . 'R',
  709 + chr(115) => chr(130) . 'S',
  710 + chr(116) => chr(130) . 'T',
  711 + chr(117) => chr(130) . 'U',
  712 + chr(118) => chr(130) . 'V',
  713 + chr(119) => chr(130) . 'W',
  714 + chr(120) => chr(130) . 'X',
  715 + chr(121) => chr(130) . 'Y',
  716 + chr(122) => chr(130) . 'Z',
  717 + chr(123) => chr(131) . 'P',
  718 + chr(124) => chr(131) . 'Q',
  719 + chr(125) => chr(131) . 'R',
  720 + chr(126) => chr(131) . 'S',
  721 + chr(127) => chr(131) . 'T'
  722 + );
  723 + $code_ext = '';
  724 + $clen = strlen($code);
  725 + for ($i = 0; $i < $clen; ++$i) {
  726 + if (ord($code[$i]) > 127) {
  727 + throw new InvalidCharacterException('Only supports till char 127');
  728 + }
  729 + $code_ext .= $encode[$code[$i]];
  730 + }
  731 + // checksum
  732 + $code_ext .= $this->checksum_code93($code_ext);
  733 + // add start and stop codes
  734 + $code = '*' . $code_ext . '*';
  735 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  736 + $k = 0;
  737 + $clen = strlen($code);
  738 + for ($i = 0; $i < $clen; ++$i) {
  739 + $char = ord($code[$i]);
  740 + if ( ! isset($chr[$char])) {
  741 + throw new InvalidCharacterException('Char ' . $char . ' is unsupported');
  742 + }
  743 + for ($j = 0; $j < 6; ++$j) {
  744 + if (($j % 2) == 0) {
  745 + $t = true; // bar
  746 + } else {
  747 + $t = false; // space
  748 + }
  749 + $w = $chr[$char][$j];
  750 + $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  751 + $bararray['maxw'] += $w;
  752 + ++$k;
  753 + }
  754 + }
  755 + $bararray['bcode'][$k] = array('t' => true, 'w' => 1, 'h' => 1, 'p' => 0);
  756 + $bararray['maxw'] += 1;
  757 +
  758 + return $bararray;
  759 + }
  760 +
  761 + /**
  762 + * Calculate CODE 93 checksum (modulo 47).
  763 + *
  764 + * @param $code (string) code to represent.
  765 + * @return string checksum code.
  766 + * @protected
  767 + */
  768 + protected function checksum_code93($code)
  769 + {
  770 + $chars = array(
  771 + '0',
  772 + '1',
  773 + '2',
  774 + '3',
  775 + '4',
  776 + '5',
  777 + '6',
  778 + '7',
  779 + '8',
  780 + '9',
  781 + 'A',
  782 + 'B',
  783 + 'C',
  784 + 'D',
  785 + 'E',
  786 + 'F',
  787 + 'G',
  788 + 'H',
  789 + 'I',
  790 + 'J',
  791 + 'K',
  792 + 'L',
  793 + 'M',
  794 + 'N',
  795 + 'O',
  796 + 'P',
  797 + 'Q',
  798 + 'R',
  799 + 'S',
  800 + 'T',
  801 + 'U',
  802 + 'V',
  803 + 'W',
  804 + 'X',
  805 + 'Y',
  806 + 'Z',
  807 + '-',
  808 + '.',
  809 + ' ',
  810 + '$',
  811 + '/',
  812 + '+',
  813 + '%',
  814 + '<',
  815 + '=',
  816 + '>',
  817 + '?'
  818 + );
  819 + // translate special characters
  820 + $code = strtr($code, chr(128) . chr(131) . chr(129) . chr(130), '<=>?');
  821 + $len = strlen($code);
  822 + // calculate check digit C
  823 + $p = 1;
  824 + $check = 0;
  825 + for ($i = ($len - 1); $i >= 0; --$i) {
  826 + $k = array_keys($chars, $code[$i]);
  827 + $check += ($k[0] * $p);
  828 + ++$p;
  829 + if ($p > 20) {
  830 + $p = 1;
  831 + }
  832 + }
  833 + $check %= 47;
  834 + $c = $chars[$check];
  835 + $code .= $c;
  836 + // calculate check digit K
  837 + $p = 1;
  838 + $check = 0;
  839 + for ($i = $len; $i >= 0; --$i) {
  840 + $k = array_keys($chars, $code[$i]);
  841 + $check += ($k[0] * $p);
  842 + ++$p;
  843 + if ($p > 15) {
  844 + $p = 1;
  845 + }
  846 + }
  847 + $check %= 47;
  848 + $k = $chars[$check];
  849 + $checksum = $c . $k;
  850 + // resto respecial characters
  851 + $checksum = strtr($checksum, '<=>?', chr(128) . chr(131) . chr(129) . chr(130));
  852 +
  853 + return $checksum;
  854 + }
  855 +
  856 + /**
  857 + * Checksum for standard 2 of 5 barcodes.
  858 + *
  859 + * @param $code (string) code to process.
  860 + * @return int checksum.
  861 + * @protected
  862 + */
  863 + protected function checksum_s25($code)
  864 + {
  865 + $len = strlen($code);
  866 + $sum = 0;
  867 + for ($i = 0; $i < $len; $i += 2) {
  868 + $sum += $code[$i];
  869 + }
  870 + $sum *= 3;
  871 + for ($i = 1; $i < $len; $i += 2) {
  872 + $sum += ($code[$i]);
  873 + }
  874 + $r = $sum % 10;
  875 + if ($r > 0) {
  876 + $r = (10 - $r);
  877 + }
  878 +
  879 + return $r;
  880 + }
  881 +
  882 + /**
  883 + * MSI.
  884 + * Variation of Plessey code, with similar applications
  885 + * Contains digits (0 to 9) and encodes the data only in the width of bars.
  886 + *
  887 + * @param $code (string) code to represent.
  888 + * @param $checksum (boolean) if true add a checksum to the code (modulo 11)
  889 + * @return array barcode representation.
  890 + * @protected
  891 + */
  892 + protected function barcode_msi($code, $checksum = false)
  893 + {
  894 + $chr['0'] = '100100100100';
  895 + $chr['1'] = '100100100110';
  896 + $chr['2'] = '100100110100';
  897 + $chr['3'] = '100100110110';
  898 + $chr['4'] = '100110100100';
  899 + $chr['5'] = '100110100110';
  900 + $chr['6'] = '100110110100';
  901 + $chr['7'] = '100110110110';
  902 + $chr['8'] = '110100100100';
  903 + $chr['9'] = '110100100110';
  904 + $chr['A'] = '110100110100';
  905 + $chr['B'] = '110100110110';
  906 + $chr['C'] = '110110100100';
  907 + $chr['D'] = '110110100110';
  908 + $chr['E'] = '110110110100';
  909 + $chr['F'] = '110110110110';
  910 + if ($checksum) {
  911 + // add checksum
  912 + $clen = strlen($code);
  913 + $p = 2;
  914 + $check = 0;
  915 + for ($i = ($clen - 1); $i >= 0; --$i) {
  916 + $check += (hexdec($code[$i]) * $p);
  917 + ++$p;
  918 + if ($p > 7) {
  919 + $p = 2;
  920 + }
  921 + }
  922 + $check %= 11;
  923 + if ($check > 0) {
  924 + $check = 11 - $check;
  925 + }
  926 + $code .= $check;
  927 + }
  928 + $seq = '110'; // left guard
  929 + $clen = strlen($code);
  930 + for ($i = 0; $i < $clen; ++$i) {
  931 + $digit = $code[$i];
  932 + if ( ! isset($chr[$digit])) {
  933 + throw new InvalidCharacterException('Char ' . $digit . ' is unsupported');
  934 + }
  935 + $seq .= $chr[$digit];
  936 + }
  937 + $seq .= '1001'; // right guard
  938 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  939 +
  940 + return $this->binseq_to_array($seq, $bararray);
  941 + }
  942 +
  943 + /**
  944 + * Standard 2 of 5 barcodes.
  945 + * Used in airline ticket marking, photofinishing
  946 + * Contains digits (0 to 9) and encodes the data only in the width of bars.
  947 + *
  948 + * @param $code (string) code to represent.
  949 + * @param $checksum (boolean) if true add a checksum to the code
  950 + * @return array barcode representation.
  951 + * @protected
  952 + */
  953 + protected function barcode_s25($code, $checksum = false)
  954 + {
  955 + $chr['0'] = '10101110111010';
  956 + $chr['1'] = '11101010101110';
  957 + $chr['2'] = '10111010101110';
  958 + $chr['3'] = '11101110101010';
  959 + $chr['4'] = '10101110101110';
  960 + $chr['5'] = '11101011101010';
  961 + $chr['6'] = '10111011101010';
  962 + $chr['7'] = '10101011101110';
  963 + $chr['8'] = '10101110111010';
  964 + $chr['9'] = '10111010111010';
  965 + if ($checksum) {
  966 + // add checksum
  967 + $code .= $this->checksum_s25($code);
  968 + }
  969 + if ((strlen($code) % 2) != 0) {
  970 + // add leading zero if code-length is odd
  971 + $code = '0' . $code;
  972 + }
  973 + $seq = '11011010';
  974 + $clen = strlen($code);
  975 + for ($i = 0; $i < $clen; ++$i) {
  976 + $digit = $code[$i];
  977 + if ( ! isset($chr[$digit])) {
  978 + throw new InvalidCharacterException('Char ' . $digit . ' is unsupported');
  979 + }
  980 + $seq .= $chr[$digit];
  981 + }
  982 + $seq .= '1101011';
  983 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  984 +
  985 + return $this->binseq_to_array($seq, $bararray);
  986 + }
  987 +
  988 + /**
  989 + * Convert binary barcode sequence to TCPDF barcode array.
  990 + *
  991 + * @param $seq (string) barcode as binary sequence.
  992 + * @param $bararray (array) barcode array.
  993 + * òparam array $bararray TCPDF barcode array to fill up
  994 + * @return array barcode representation.
  995 + * @protected
  996 + */
  997 + protected function binseq_to_array($seq, $bararray)
  998 + {
  999 + $len = strlen($seq);
  1000 + $w = 0;
  1001 + $k = 0;
  1002 + for ($i = 0; $i < $len; ++$i) {
  1003 + $w += 1;
  1004 + if (($i == ($len - 1)) OR (($i < ($len - 1)) AND ($seq[$i] != $seq[($i + 1)]))) {
  1005 + if ($seq[$i] == '1') {
  1006 + $t = true; // bar
  1007 + } else {
  1008 + $t = false; // space
  1009 + }
  1010 + $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  1011 + $bararray['maxw'] += $w;
  1012 + ++$k;
  1013 + $w = 0;
  1014 + }
  1015 + }
  1016 +
  1017 + return $bararray;
  1018 + }
  1019 +
  1020 + /**
  1021 + * Interleaved 2 of 5 barcodes.
  1022 + * Compact numeric code, widely used in industry, air cargo
  1023 + * Contains digits (0 to 9) and encodes the data in the width of both bars and spaces.
  1024 + *
  1025 + * @param $code (string) code to represent.
  1026 + * @param $checksum (boolean) if true add a checksum to the code
  1027 + * @return array barcode representation.
  1028 + * @protected
  1029 + */
  1030 + protected function barcode_i25($code, $checksum = false)
  1031 + {
  1032 + $chr['0'] = '11221';
  1033 + $chr['1'] = '21112';
  1034 + $chr['2'] = '12112';
  1035 + $chr['3'] = '22111';
  1036 + $chr['4'] = '11212';
  1037 + $chr['5'] = '21211';
  1038 + $chr['6'] = '12211';
  1039 + $chr['7'] = '11122';
  1040 + $chr['8'] = '21121';
  1041 + $chr['9'] = '12121';
  1042 + $chr['A'] = '11';
  1043 + $chr['Z'] = '21';
  1044 + if ($checksum) {
  1045 + // add checksum
  1046 + $code .= $this->checksum_s25($code);
  1047 + }
  1048 + if ((strlen($code) % 2) != 0) {
  1049 + // add leading zero if code-length is odd
  1050 + $code = '0' . $code;
  1051 + }
  1052 + // add start and stop codes
  1053 + $code = 'AA' . strtolower($code) . 'ZA';
  1054 +
  1055 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1056 + $k = 0;
  1057 + $clen = strlen($code);
  1058 + for ($i = 0; $i < $clen; $i = ($i + 2)) {
  1059 + $char_bar = $code[$i];
  1060 + $char_space = $code[$i + 1];
  1061 + if ( ! isset($chr[$char_bar]) || ! isset($chr[$char_space])) {
  1062 + throw new InvalidCharacterException();
  1063 + }
  1064 + // create a bar-space sequence
  1065 + $seq = '';
  1066 + $chrlen = strlen($chr[$char_bar]);
  1067 + for ($s = 0; $s < $chrlen; $s++) {
  1068 + $seq .= $chr[$char_bar][$s] . $chr[$char_space][$s];
  1069 + }
  1070 + $seqlen = strlen($seq);
  1071 + for ($j = 0; $j < $seqlen; ++$j) {
  1072 + if (($j % 2) == 0) {
  1073 + $t = true; // bar
  1074 + } else {
  1075 + $t = false; // space
  1076 + }
  1077 + $w = $seq[$j];
  1078 + $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  1079 + $bararray['maxw'] += $w;
  1080 + ++$k;
  1081 + }
  1082 + }
  1083 +
  1084 + return $bararray;
  1085 + }
  1086 +
  1087 + /**
  1088 + * C128 barcodes.
  1089 + * Very capable code, excellent density, high reliability; in very wide use world-wide
  1090 + *
  1091 + * @param $code (string) code to represent.
  1092 + * @param $type (string) barcode type: A, B, C or empty for automatic switch (AUTO mode)
  1093 + * @return array barcode representation.
  1094 + * @protected
  1095 + */
  1096 + protected function barcode_c128($code, $type = '')
  1097 + {
  1098 + $chr = array(
  1099 + '212222', /* 00 */
  1100 + '222122', /* 01 */
  1101 + '222221', /* 02 */
  1102 + '121223', /* 03 */
  1103 + '121322', /* 04 */
  1104 + '131222', /* 05 */
  1105 + '122213', /* 06 */
  1106 + '122312', /* 07 */
  1107 + '132212', /* 08 */
  1108 + '221213', /* 09 */
  1109 + '221312', /* 10 */
  1110 + '231212', /* 11 */
  1111 + '112232', /* 12 */
  1112 + '122132', /* 13 */
  1113 + '122231', /* 14 */
  1114 + '113222', /* 15 */
  1115 + '123122', /* 16 */
  1116 + '123221', /* 17 */
  1117 + '223211', /* 18 */
  1118 + '221132', /* 19 */
  1119 + '221231', /* 20 */
  1120 + '213212', /* 21 */
  1121 + '223112', /* 22 */
  1122 + '312131', /* 23 */
  1123 + '311222', /* 24 */
  1124 + '321122', /* 25 */
  1125 + '321221', /* 26 */
  1126 + '312212', /* 27 */
  1127 + '322112', /* 28 */
  1128 + '322211', /* 29 */
  1129 + '212123', /* 30 */
  1130 + '212321', /* 31 */
  1131 + '232121', /* 32 */
  1132 + '111323', /* 33 */
  1133 + '131123', /* 34 */
  1134 + '131321', /* 35 */
  1135 + '112313', /* 36 */
  1136 + '132113', /* 37 */
  1137 + '132311', /* 38 */
  1138 + '211313', /* 39 */
  1139 + '231113', /* 40 */
  1140 + '231311', /* 41 */
  1141 + '112133', /* 42 */
  1142 + '112331', /* 43 */
  1143 + '132131', /* 44 */
  1144 + '113123', /* 45 */
  1145 + '113321', /* 46 */
  1146 + '133121', /* 47 */
  1147 + '313121', /* 48 */
  1148 + '211331', /* 49 */
  1149 + '231131', /* 50 */
  1150 + '213113', /* 51 */
  1151 + '213311', /* 52 */
  1152 + '213131', /* 53 */
  1153 + '311123', /* 54 */
  1154 + '311321', /* 55 */
  1155 + '331121', /* 56 */
  1156 + '312113', /* 57 */
  1157 + '312311', /* 58 */
  1158 + '332111', /* 59 */
  1159 + '314111', /* 60 */
  1160 + '221411', /* 61 */
  1161 + '431111', /* 62 */
  1162 + '111224', /* 63 */
  1163 + '111422', /* 64 */
  1164 + '121124', /* 65 */
  1165 + '121421', /* 66 */
  1166 + '141122', /* 67 */
  1167 + '141221', /* 68 */
  1168 + '112214', /* 69 */
  1169 + '112412', /* 70 */
  1170 + '122114', /* 71 */
  1171 + '122411', /* 72 */
  1172 + '142112', /* 73 */
  1173 + '142211', /* 74 */
  1174 + '241211', /* 75 */
  1175 + '221114', /* 76 */
  1176 + '413111', /* 77 */
  1177 + '241112', /* 78 */
  1178 + '134111', /* 79 */
  1179 + '111242', /* 80 */
  1180 + '121142', /* 81 */
  1181 + '121241', /* 82 */
  1182 + '114212', /* 83 */
  1183 + '124112', /* 84 */
  1184 + '124211', /* 85 */
  1185 + '411212', /* 86 */
  1186 + '421112', /* 87 */
  1187 + '421211', /* 88 */
  1188 + '212141', /* 89 */
  1189 + '214121', /* 90 */
  1190 + '412121', /* 91 */
  1191 + '111143', /* 92 */
  1192 + '111341', /* 93 */
  1193 + '131141', /* 94 */
  1194 + '114113', /* 95 */
  1195 + '114311', /* 96 */
  1196 + '411113', /* 97 */
  1197 + '411311', /* 98 */
  1198 + '113141', /* 99 */
  1199 + '114131', /* 100 */
  1200 + '311141', /* 101 */
  1201 + '411131', /* 102 */
  1202 + '211412', /* 103 START A */
  1203 + '211214', /* 104 START B */
  1204 + '211232', /* 105 START C */
  1205 + '233111', /* STOP */
  1206 + '200000' /* END */
  1207 + );
  1208 + // ASCII characters for code A (ASCII 00 - 95)
  1209 + $keys_a = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
  1210 + $keys_a .= chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8) . chr(9);
  1211 + $keys_a .= chr(10) . chr(11) . chr(12) . chr(13) . chr(14) . chr(15) . chr(16) . chr(17) . chr(18) . chr(19);
  1212 + $keys_a .= chr(20) . chr(21) . chr(22) . chr(23) . chr(24) . chr(25) . chr(26) . chr(27) . chr(28) . chr(29);
  1213 + $keys_a .= chr(30) . chr(31);
  1214 + // ASCII characters for code B (ASCII 32 - 127)
  1215 + $keys_b = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~' . chr(127);
  1216 + // special codes
  1217 + $fnc_a = array(241 => 102, 242 => 97, 243 => 96, 244 => 101);
  1218 + $fnc_b = array(241 => 102, 242 => 97, 243 => 96, 244 => 100);
  1219 + // array of symbols
  1220 + $code_data = array();
  1221 + // length of the code
  1222 + $len = strlen($code);
  1223 + switch (strtoupper($type)) {
  1224 + case 'A': { // MODE A
  1225 + $startid = 103;
  1226 + for ($i = 0; $i < $len; ++$i) {
  1227 + $char = $code[$i];
  1228 + $char_id = ord($char);
  1229 + if (($char_id >= 241) AND ($char_id <= 244)) {
  1230 + $code_data[] = $fnc_a[$char_id];
  1231 + } elseif (($char_id >= 0) AND ($char_id <= 95)) {
  1232 + $code_data[] = strpos($keys_a, $char);
  1233 + } else {
  1234 + throw new InvalidCharacterException('Char ' . $char . ' is unsupported');
  1235 + }
  1236 + }
  1237 + break;
  1238 + }
  1239 + case 'B': { // MODE B
  1240 + $startid = 104;
  1241 + for ($i = 0; $i < $len; ++$i) {
  1242 + $char = $code[$i];
  1243 + $char_id = ord($char);
  1244 + if (($char_id >= 241) AND ($char_id <= 244)) {
  1245 + $code_data[] = $fnc_b[$char_id];
  1246 + } elseif (($char_id >= 32) AND ($char_id <= 127)) {
  1247 + $code_data[] = strpos($keys_b, $char);
  1248 + } else {
  1249 + throw new InvalidCharacterException('Char ' . $char . ' is unsupported');
  1250 + }
  1251 + }
  1252 + break;
  1253 + }
  1254 + case 'C': { // MODE C
  1255 + $startid = 105;
  1256 + if (ord($code[0]) == 241) {
  1257 + $code_data[] = 102;
  1258 + $code = substr($code, 1);
  1259 + --$len;
  1260 + }
  1261 + if (($len % 2) != 0) {
  1262 + throw new InvalidLengthException('Length must be even');
  1263 + }
  1264 + for ($i = 0; $i < $len; $i += 2) {
  1265 + $chrnum = $code[$i] . $code[$i + 1];
  1266 + if (preg_match('/([0-9]{2})/', $chrnum) > 0) {
  1267 + $code_data[] = intval($chrnum);
  1268 + } else {
  1269 + throw new InvalidCharacterException();
  1270 + }
  1271 + }
  1272 + break;
  1273 + }
  1274 + default: { // MODE AUTO
  1275 + // split code into sequences
  1276 + $sequence = array();
  1277 + // get numeric sequences (if any)
  1278 + $numseq = array();
  1279 + preg_match_all('/([0-9]{4,})/', $code, $numseq, PREG_OFFSET_CAPTURE);
  1280 + if (isset($numseq[1]) AND ! empty($numseq[1])) {
  1281 + $end_offset = 0;
  1282 + foreach ($numseq[1] as $val) {
  1283 + $offset = $val[1];
  1284 +
  1285 + // numeric sequence
  1286 + $slen = strlen($val[0]);
  1287 + if (($slen % 2) != 0) {
  1288 + // the length must be even
  1289 + ++$offset;
  1290 + $val[0] = substr($val[0],1);
  1291 + }
  1292 +
  1293 + if ($offset > $end_offset) {
  1294 + // non numeric sequence
  1295 + $sequence = array_merge($sequence,
  1296 + $this->get128ABsequence(substr($code, $end_offset, ($offset - $end_offset))));
  1297 + }
  1298 + // numeric sequence fallback
  1299 + $slen = strlen($val[0]);
  1300 + if (($slen % 2) != 0) {
  1301 + // the length must be even
  1302 + --$slen;
  1303 + }
  1304 + $sequence[] = array('C', substr($code, $offset, $slen), $slen);
  1305 + $end_offset = $offset + $slen;
  1306 + }
  1307 + if ($end_offset < $len) {
  1308 + $sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset)));
  1309 + }
  1310 + } else {
  1311 + // text code (non C mode)
  1312 + $sequence = array_merge($sequence, $this->get128ABsequence($code));
  1313 + }
  1314 + // process the sequence
  1315 + foreach ($sequence as $key => $seq) {
  1316 + switch ($seq[0]) {
  1317 + case 'A': {
  1318 + if ($key == 0) {
  1319 + $startid = 103;
  1320 + } elseif ($sequence[($key - 1)][0] != 'A') {
  1321 + if (($seq[2] == 1) AND ($key > 0) AND ($sequence[($key - 1)][0] == 'B') AND ( ! isset($sequence[($key - 1)][3]))) {
  1322 + // single character shift
  1323 + $code_data[] = 98;
  1324 + // mark shift
  1325 + $sequence[$key][3] = true;
  1326 + } elseif ( ! isset($sequence[($key - 1)][3])) {
  1327 + $code_data[] = 101;
  1328 + }
  1329 + }
  1330 + for ($i = 0; $i < $seq[2]; ++$i) {
  1331 + $char = $seq[1][$i];
  1332 + $char_id = ord($char);
  1333 + if (($char_id >= 241) AND ($char_id <= 244)) {
  1334 + $code_data[] = $fnc_a[$char_id];
  1335 + } else {
  1336 + $code_data[] = strpos($keys_a, $char);
  1337 + }
  1338 + }
  1339 + break;
  1340 + }
  1341 + case 'B': {
  1342 + if ($key == 0) {
  1343 + $tmpchr = ord($seq[1][0]);
  1344 + if (($seq[2] == 1) AND ($tmpchr >= 241) AND ($tmpchr <= 244) AND isset($sequence[($key + 1)]) AND ($sequence[($key + 1)][0] != 'B')) {
  1345 + switch ($sequence[($key + 1)][0]) {
  1346 + case 'A': {
  1347 + $startid = 103;
  1348 + $sequence[$key][0] = 'A';
  1349 + $code_data[] = $fnc_a[$tmpchr];
  1350 + break;
  1351 + }
  1352 + case 'C': {
  1353 + $startid = 105;
  1354 + $sequence[$key][0] = 'C';
  1355 + $code_data[] = $fnc_a[$tmpchr];
  1356 + break;
  1357 + }
  1358 + }
  1359 + break;
  1360 + } else {
  1361 + $startid = 104;
  1362 + }
  1363 + } elseif ($sequence[($key - 1)][0] != 'B') {
  1364 + if (($seq[2] == 1) AND ($key > 0) AND ($sequence[($key - 1)][0] == 'A') AND ( ! isset($sequence[($key - 1)][3]))) {
  1365 + // single character shift
  1366 + $code_data[] = 98;
  1367 + // mark shift
  1368 + $sequence[$key][3] = true;
  1369 + } elseif ( ! isset($sequence[($key - 1)][3])) {
  1370 + $code_data[] = 100;
  1371 + }
  1372 + }
  1373 + for ($i = 0; $i < $seq[2]; ++$i) {
  1374 + $char = $seq[1][$i];
  1375 + $char_id = ord($char);
  1376 + if (($char_id >= 241) AND ($char_id <= 244)) {
  1377 + $code_data[] = $fnc_b[$char_id];
  1378 + } else {
  1379 + $code_data[] = strpos($keys_b, $char);
  1380 + }
  1381 + }
  1382 + break;
  1383 + }
  1384 + case 'C': {
  1385 + if ($key == 0) {
  1386 + $startid = 105;
  1387 + } elseif ($sequence[($key - 1)][0] != 'C') {
  1388 + $code_data[] = 99;
  1389 + }
  1390 + for ($i = 0; $i < $seq[2]; $i += 2) {
  1391 + $chrnum = $seq[1][$i] . $seq[1][$i + 1];
  1392 + $code_data[] = intval($chrnum);
  1393 + }
  1394 + break;
  1395 + }
  1396 + }
  1397 + }
  1398 + }
  1399 + }
  1400 + // calculate check character
  1401 + $sum = $startid;
  1402 + foreach ($code_data as $key => $val) {
  1403 + $sum += ($val * ($key + 1));
  1404 + }
  1405 + // add check character
  1406 + $code_data[] = ($sum % 103);
  1407 + // add stop sequence
  1408 + $code_data[] = 106;
  1409 + $code_data[] = 107;
  1410 + // add start code at the beginning
  1411 + array_unshift($code_data, $startid);
  1412 + // build barcode array
  1413 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1414 + foreach ($code_data as $val) {
  1415 + $seq = $chr[$val];
  1416 + for ($j = 0; $j < 6; ++$j) {
  1417 + if (($j % 2) == 0) {
  1418 + $t = true; // bar
  1419 + } else {
  1420 + $t = false; // space
  1421 + }
  1422 + $w = $seq[$j];
  1423 + $bararray['bcode'][] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  1424 + $bararray['maxw'] += $w;
  1425 + }
  1426 + }
  1427 +
  1428 + return $bararray;
  1429 + }
  1430 +
  1431 + /**
  1432 + * Split text code in A/B sequence for 128 code
  1433 + *
  1434 + * @param $code (string) code to split.
  1435 + * @return array sequence
  1436 + * @protected
  1437 + */
  1438 + protected function get128ABsequence($code)
  1439 + {
  1440 + $len = strlen($code);
  1441 + $sequence = array();
  1442 + // get A sequences (if any)
  1443 + $numseq = array();
  1444 + preg_match_all('/([\x00-\x1f])/', $code, $numseq, PREG_OFFSET_CAPTURE);
  1445 + if (isset($numseq[1]) AND ! empty($numseq[1])) {
  1446 + $end_offset = 0;
  1447 + foreach ($numseq[1] as $val) {
  1448 + $offset = $val[1];
  1449 + if ($offset > $end_offset) {
  1450 + // B sequence
  1451 + $sequence[] = array(
  1452 + 'B',
  1453 + substr($code, $end_offset, ($offset - $end_offset)),
  1454 + ($offset - $end_offset)
  1455 + );
  1456 + }
  1457 + // A sequence
  1458 + $slen = strlen($val[0]);
  1459 + $sequence[] = array('A', substr($code, $offset, $slen), $slen);
  1460 + $end_offset = $offset + $slen;
  1461 + }
  1462 + if ($end_offset < $len) {
  1463 + $sequence[] = array('B', substr($code, $end_offset), ($len - $end_offset));
  1464 + }
  1465 + } else {
  1466 + // only B sequence
  1467 + $sequence[] = array('B', $code, $len);
  1468 + }
  1469 +
  1470 + return $sequence;
  1471 + }
  1472 +
  1473 + /**
  1474 + * EAN13 and UPC-A barcodes.
  1475 + * EAN13: European Article Numbering international retail product code
  1476 + * UPC-A: Universal product code seen on almost all retail products in the USA and Canada
  1477 + * UPC-E: Short version of UPC symbol
  1478 + *
  1479 + * @param $code (string) code to represent.
  1480 + * @param $len (string) barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
  1481 + * @return array barcode representation.
  1482 + * @protected
  1483 + */
  1484 + protected function barcode_eanupc($code, $len = 13)
  1485 + {
  1486 + $upce = false;
  1487 + if ($len == 6) {
  1488 + $len = 12; // UPC-A
  1489 + $upce = true; // UPC-E mode
  1490 + }
  1491 + $data_len = $len - 1;
  1492 + //Padding
  1493 + $code = str_pad($code, $data_len, '0', STR_PAD_LEFT);
  1494 + $code_len = strlen($code);
  1495 + // calculate check digit
  1496 + $sum_a = 0;
  1497 + for ($i = 1; $i < $data_len; $i += 2) {
  1498 + $sum_a += $code[$i];
  1499 + }
  1500 + if ($len > 12) {
  1501 + $sum_a *= 3;
  1502 + }
  1503 + $sum_b = 0;
  1504 + for ($i = 0; $i < $data_len; $i += 2) {
  1505 + $sum_b += intval(($code[$i]));
  1506 + }
  1507 + if ($len < 13) {
  1508 + $sum_b *= 3;
  1509 + }
  1510 + $r = ($sum_a + $sum_b) % 10;
  1511 + if ($r > 0) {
  1512 + $r = (10 - $r);
  1513 + }
  1514 + if ($code_len == $data_len) {
  1515 + // add check digit
  1516 + $code .= $r;
  1517 + } elseif ($r !== intval($code[$data_len])) {
  1518 + throw new InvalidCheckDigitException();
  1519 + }
  1520 + if ($len == 12) {
  1521 + // UPC-A
  1522 + $code = '0' . $code;
  1523 + ++$len;
  1524 + }
  1525 + if ($upce) {
  1526 + // convert UPC-A to UPC-E
  1527 + $tmp = substr($code, 4, 3);
  1528 + if (($tmp == '000') OR ($tmp == '100') OR ($tmp == '200')) {
  1529 + // manufacturer code ends in 000, 100, or 200
  1530 + $upce_code = substr($code, 2, 2) . substr($code, 9, 3) . substr($code, 4, 1);
  1531 + } else {
  1532 + $tmp = substr($code, 5, 2);
  1533 + if ($tmp == '00') {
  1534 + // manufacturer code ends in 00
  1535 + $upce_code = substr($code, 2, 3) . substr($code, 10, 2) . '3';
  1536 + } else {
  1537 + $tmp = substr($code, 6, 1);
  1538 + if ($tmp == '0') {
  1539 + // manufacturer code ends in 0
  1540 + $upce_code = substr($code, 2, 4) . substr($code, 11, 1) . '4';
  1541 + } else {
  1542 + // manufacturer code does not end in zero
  1543 + $upce_code = substr($code, 2, 5) . substr($code, 11, 1);
  1544 + }
  1545 + }
  1546 + }
  1547 + }
  1548 + //Convert digits to bars
  1549 + $codes = array(
  1550 + 'A' => array( // left odd parity
  1551 + '0' => '0001101',
  1552 + '1' => '0011001',
  1553 + '2' => '0010011',
  1554 + '3' => '0111101',
  1555 + '4' => '0100011',
  1556 + '5' => '0110001',
  1557 + '6' => '0101111',
  1558 + '7' => '0111011',
  1559 + '8' => '0110111',
  1560 + '9' => '0001011'
  1561 + ),
  1562 + 'B' => array( // left even parity
  1563 + '0' => '0100111',
  1564 + '1' => '0110011',
  1565 + '2' => '0011011',
  1566 + '3' => '0100001',
  1567 + '4' => '0011101',
  1568 + '5' => '0111001',
  1569 + '6' => '0000101',
  1570 + '7' => '0010001',
  1571 + '8' => '0001001',
  1572 + '9' => '0010111'
  1573 + ),
  1574 + 'C' => array( // right
  1575 + '0' => '1110010',
  1576 + '1' => '1100110',
  1577 + '2' => '1101100',
  1578 + '3' => '1000010',
  1579 + '4' => '1011100',
  1580 + '5' => '1001110',
  1581 + '6' => '1010000',
  1582 + '7' => '1000100',
  1583 + '8' => '1001000',
  1584 + '9' => '1110100'
  1585 + )
  1586 + );
  1587 + $parities = array(
  1588 + '0' => array('A', 'A', 'A', 'A', 'A', 'A'),
  1589 + '1' => array('A', 'A', 'B', 'A', 'B', 'B'),
  1590 + '2' => array('A', 'A', 'B', 'B', 'A', 'B'),
  1591 + '3' => array('A', 'A', 'B', 'B', 'B', 'A'),
  1592 + '4' => array('A', 'B', 'A', 'A', 'B', 'B'),
  1593 + '5' => array('A', 'B', 'B', 'A', 'A', 'B'),
  1594 + '6' => array('A', 'B', 'B', 'B', 'A', 'A'),
  1595 + '7' => array('A', 'B', 'A', 'B', 'A', 'B'),
  1596 + '8' => array('A', 'B', 'A', 'B', 'B', 'A'),
  1597 + '9' => array('A', 'B', 'B', 'A', 'B', 'A')
  1598 + );
  1599 + $upce_parities = array();
  1600 + $upce_parities[0] = array(
  1601 + '0' => array('B', 'B', 'B', 'A', 'A', 'A'),
  1602 + '1' => array('B', 'B', 'A', 'B', 'A', 'A'),
  1603 + '2' => array('B', 'B', 'A', 'A', 'B', 'A'),
  1604 + '3' => array('B', 'B', 'A', 'A', 'A', 'B'),
  1605 + '4' => array('B', 'A', 'B', 'B', 'A', 'A'),
  1606 + '5' => array('B', 'A', 'A', 'B', 'B', 'A'),
  1607 + '6' => array('B', 'A', 'A', 'A', 'B', 'B'),
  1608 + '7' => array('B', 'A', 'B', 'A', 'B', 'A'),
  1609 + '8' => array('B', 'A', 'B', 'A', 'A', 'B'),
  1610 + '9' => array('B', 'A', 'A', 'B', 'A', 'B')
  1611 + );
  1612 + $upce_parities[1] = array(
  1613 + '0' => array('A', 'A', 'A', 'B', 'B', 'B'),
  1614 + '1' => array('A', 'A', 'B', 'A', 'B', 'B'),
  1615 + '2' => array('A', 'A', 'B', 'B', 'A', 'B'),
  1616 + '3' => array('A', 'A', 'B', 'B', 'B', 'A'),
  1617 + '4' => array('A', 'B', 'A', 'A', 'B', 'B'),
  1618 + '5' => array('A', 'B', 'B', 'A', 'A', 'B'),
  1619 + '6' => array('A', 'B', 'B', 'B', 'A', 'A'),
  1620 + '7' => array('A', 'B', 'A', 'B', 'A', 'B'),
  1621 + '8' => array('A', 'B', 'A', 'B', 'B', 'A'),
  1622 + '9' => array('A', 'B', 'B', 'A', 'B', 'A')
  1623 + );
  1624 + $k = 0;
  1625 + $seq = '101'; // left guard bar
  1626 + if ($upce) {
  1627 + $bararray = array('code' => $upce_code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1628 + $p = $upce_parities[$code[1]][$r];
  1629 + for ($i = 0; $i < 6; ++$i) {
  1630 + $seq .= $codes[$p[$i]][$upce_code[$i]];
  1631 + }
  1632 + $seq .= '010101'; // right guard bar
  1633 + } else {
  1634 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1635 + $half_len = intval(ceil($len / 2));
  1636 + if ($len == 8) {
  1637 + for ($i = 0; $i < $half_len; ++$i) {
  1638 + $seq .= $codes['A'][$code[$i]];
  1639 + }
  1640 + } else {
  1641 + $p = $parities[$code[0]];
  1642 + for ($i = 1; $i < $half_len; ++$i) {
  1643 + $seq .= $codes[$p[$i - 1]][$code[$i]];
  1644 + }
  1645 + }
  1646 + $seq .= '01010'; // center guard bar
  1647 + for ($i = $half_len; $i < $len; ++$i) {
  1648 + if ( ! isset($codes['C'][$code[$i]])) {
  1649 + throw new InvalidCharacterException('Char ' . $code[$i] . ' not allowed');
  1650 + }
  1651 + $seq .= $codes['C'][$code[$i]];
  1652 + }
  1653 + $seq .= '101'; // right guard bar
  1654 + }
  1655 + $clen = strlen($seq);
  1656 + $w = 0;
  1657 + for ($i = 0; $i < $clen; ++$i) {
  1658 + $w += 1;
  1659 + if (($i == ($clen - 1)) OR (($i < ($clen - 1)) AND ($seq[$i] != $seq[($i + 1)]))) {
  1660 + if ($seq[$i] == '1') {
  1661 + $t = true; // bar
  1662 + } else {
  1663 + $t = false; // space
  1664 + }
  1665 + $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  1666 + $bararray['maxw'] += $w;
  1667 + ++$k;
  1668 + $w = 0;
  1669 + }
  1670 + }
  1671 +
  1672 + return $bararray;
  1673 + }
  1674 +
  1675 + /**
  1676 + * UPC-Based Extensions
  1677 + * 2-Digit Ext.: Used to indicate magazines and newspaper issue numbers
  1678 + * 5-Digit Ext.: Used to mark suggested retail price of books
  1679 + *
  1680 + * @param $code (string) code to represent.
  1681 + * @param $len (string) barcode type: 2 = 2-Digit, 5 = 5-Digit
  1682 + * @return array barcode representation.
  1683 + * @protected
  1684 + */
  1685 + protected function barcode_eanext($code, $len = 5)
  1686 + {
  1687 + //Padding
  1688 + $code = str_pad($code, $len, '0', STR_PAD_LEFT);
  1689 + // calculate check digit
  1690 + if ($len == 2) {
  1691 + $r = $code % 4;
  1692 + } elseif ($len == 5) {
  1693 + $r = (3 * ($code[0] + $code[2] + $code[4])) + (9 * ($code[1] + $code[3]));
  1694 + $r %= 10;
  1695 + } else {
  1696 + throw new InvalidCheckDigitException();
  1697 + }
  1698 + //Convert digits to bars
  1699 + $codes = array(
  1700 + 'A' => array( // left odd parity
  1701 + '0' => '0001101',
  1702 + '1' => '0011001',
  1703 + '2' => '0010011',
  1704 + '3' => '0111101',
  1705 + '4' => '0100011',
  1706 + '5' => '0110001',
  1707 + '6' => '0101111',
  1708 + '7' => '0111011',
  1709 + '8' => '0110111',
  1710 + '9' => '0001011'
  1711 + ),
  1712 + 'B' => array( // left even parity
  1713 + '0' => '0100111',
  1714 + '1' => '0110011',
  1715 + '2' => '0011011',
  1716 + '3' => '0100001',
  1717 + '4' => '0011101',
  1718 + '5' => '0111001',
  1719 + '6' => '0000101',
  1720 + '7' => '0010001',
  1721 + '8' => '0001001',
  1722 + '9' => '0010111'
  1723 + )
  1724 + );
  1725 + $parities = array();
  1726 + $parities[2] = array(
  1727 + '0' => array('A', 'A'),
  1728 + '1' => array('A', 'B'),
  1729 + '2' => array('B', 'A'),
  1730 + '3' => array('B', 'B')
  1731 + );
  1732 + $parities[5] = array(
  1733 + '0' => array('B', 'B', 'A', 'A', 'A'),
  1734 + '1' => array('B', 'A', 'B', 'A', 'A'),
  1735 + '2' => array('B', 'A', 'A', 'B', 'A'),
  1736 + '3' => array('B', 'A', 'A', 'A', 'B'),
  1737 + '4' => array('A', 'B', 'B', 'A', 'A'),
  1738 + '5' => array('A', 'A', 'B', 'B', 'A'),
  1739 + '6' => array('A', 'A', 'A', 'B', 'B'),
  1740 + '7' => array('A', 'B', 'A', 'B', 'A'),
  1741 + '8' => array('A', 'B', 'A', 'A', 'B'),
  1742 + '9' => array('A', 'A', 'B', 'A', 'B')
  1743 + );
  1744 + $p = $parities[$len][$r];
  1745 + $seq = '1011'; // left guard bar
  1746 + $seq .= $codes[$p[0]][$code[0]];
  1747 + for ($i = 1; $i < $len; ++$i) {
  1748 + $seq .= '01'; // separator
  1749 + $seq .= $codes[$p[$i]][$code[$i]];
  1750 + }
  1751 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1752 +
  1753 + return $this->binseq_to_array($seq, $bararray);
  1754 + }
  1755 +
  1756 + /**
  1757 + * POSTNET and PLANET barcodes.
  1758 + * Used by U.S. Postal Service for automated mail sorting
  1759 + *
  1760 + * @param $code (string) zip code to represent. Must be a string containing a zip code of the form DDDDD or
  1761 + * DDDDD-DDDD.
  1762 + * @param $planet (boolean) if true print the PLANET barcode, otherwise print POSTNET
  1763 + * @return array barcode representation.
  1764 + * @protected
  1765 + */
  1766 + protected function barcode_postnet($code, $planet = false)
  1767 + {
  1768 + // bar length
  1769 + if ($planet) {
  1770 + $barlen = Array(
  1771 + 0 => Array(1, 1, 2, 2, 2),
  1772 + 1 => Array(2, 2, 2, 1, 1),
  1773 + 2 => Array(2, 2, 1, 2, 1),
  1774 + 3 => Array(2, 2, 1, 1, 2),
  1775 + 4 => Array(2, 1, 2, 2, 1),
  1776 + 5 => Array(2, 1, 2, 1, 2),
  1777 + 6 => Array(2, 1, 1, 2, 2),
  1778 + 7 => Array(1, 2, 2, 2, 1),
  1779 + 8 => Array(1, 2, 2, 1, 2),
  1780 + 9 => Array(1, 2, 1, 2, 2)
  1781 + );
  1782 + } else {
  1783 + $barlen = Array(
  1784 + 0 => Array(2, 2, 1, 1, 1),
  1785 + 1 => Array(1, 1, 1, 2, 2),
  1786 + 2 => Array(1, 1, 2, 1, 2),
  1787 + 3 => Array(1, 1, 2, 2, 1),
  1788 + 4 => Array(1, 2, 1, 1, 2),
  1789 + 5 => Array(1, 2, 1, 2, 1),
  1790 + 6 => Array(1, 2, 2, 1, 1),
  1791 + 7 => Array(2, 1, 1, 1, 2),
  1792 + 8 => Array(2, 1, 1, 2, 1),
  1793 + 9 => Array(2, 1, 2, 1, 1)
  1794 + );
  1795 + }
  1796 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array());
  1797 + $k = 0;
  1798 + $code = str_replace('-', '', $code);
  1799 + $code = str_replace(' ', '', $code);
  1800 + $len = strlen($code);
  1801 + // calculate checksum
  1802 + $sum = 0;
  1803 + for ($i = 0; $i < $len; ++$i) {
  1804 + $sum += intval($code[$i]);
  1805 + }
  1806 + $chkd = ($sum % 10);
  1807 + if ($chkd > 0) {
  1808 + $chkd = (10 - $chkd);
  1809 + }
  1810 + $code .= $chkd;
  1811 + $len = strlen($code);
  1812 + // start bar
  1813 + $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
  1814 + $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  1815 + $bararray['maxw'] += 2;
  1816 + for ($i = 0; $i < $len; ++$i) {
  1817 + for ($j = 0; $j < 5; ++$j) {
  1818 + $h = $barlen[$code[$i]][$j];
  1819 + $p = floor(1 / $h);
  1820 + $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
  1821 + $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  1822 + $bararray['maxw'] += 2;
  1823 + }
  1824 + }
  1825 + // end bar
  1826 + $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
  1827 + $bararray['maxw'] += 1;
  1828 +
  1829 + return $bararray;
  1830 + }
  1831 +
  1832 + /**
  1833 + * RMS4CC - CBC - KIX
  1834 + * RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) - KIX (Klant index - Customer index)
  1835 + * RM4SCC is the name of the barcode symbology used by the Royal Mail for its Cleanmail service.
  1836 + *
  1837 + * @param $code (string) code to print
  1838 + * @param $kix (boolean) if true prints the KIX variation (doesn't use the start and end symbols, and the checksum)
  1839 + * - in this case the house number must be sufficed with an X and placed at the end of the code.
  1840 + * @return array barcode representation.
  1841 + * @protected
  1842 + */
  1843 + protected function barcode_rms4cc($code, $kix = false)
  1844 + {
  1845 + $notkix = ! $kix;
  1846 + // bar mode
  1847 + // 1 = pos 1, length 2
  1848 + // 2 = pos 1, length 3
  1849 + // 3 = pos 2, length 1
  1850 + // 4 = pos 2, length 2
  1851 + $barmode = array(
  1852 + '0' => array(3, 3, 2, 2),
  1853 + '1' => array(3, 4, 1, 2),
  1854 + '2' => array(3, 4, 2, 1),
  1855 + '3' => array(4, 3, 1, 2),
  1856 + '4' => array(4, 3, 2, 1),
  1857 + '5' => array(4, 4, 1, 1),
  1858 + '6' => array(3, 1, 4, 2),
  1859 + '7' => array(3, 2, 3, 2),
  1860 + '8' => array(3, 2, 4, 1),
  1861 + '9' => array(4, 1, 3, 2),
  1862 + 'A' => array(4, 1, 4, 1),
  1863 + 'B' => array(4, 2, 3, 1),
  1864 + 'C' => array(3, 1, 2, 4),
  1865 + 'D' => array(3, 2, 1, 4),
  1866 + 'E' => array(3, 2, 2, 3),
  1867 + 'F' => array(4, 1, 1, 4),
  1868 + 'G' => array(4, 1, 2, 3),
  1869 + 'H' => array(4, 2, 1, 3),
  1870 + 'I' => array(1, 3, 4, 2),
  1871 + 'J' => array(1, 4, 3, 2),
  1872 + 'K' => array(1, 4, 4, 1),
  1873 + 'L' => array(2, 3, 3, 2),
  1874 + 'M' => array(2, 3, 4, 1),
  1875 + 'N' => array(2, 4, 3, 1),
  1876 + 'O' => array(1, 3, 2, 4),
  1877 + 'P' => array(1, 4, 1, 4),
  1878 + 'Q' => array(1, 4, 2, 3),
  1879 + 'R' => array(2, 3, 1, 4),
  1880 + 'S' => array(2, 3, 2, 3),
  1881 + 'T' => array(2, 4, 1, 3),
  1882 + 'U' => array(1, 1, 4, 4),
  1883 + 'V' => array(1, 2, 3, 4),
  1884 + 'W' => array(1, 2, 4, 3),
  1885 + 'X' => array(2, 1, 3, 4),
  1886 + 'Y' => array(2, 1, 4, 3),
  1887 + 'Z' => array(2, 2, 3, 3)
  1888 + );
  1889 + $code = strtoupper($code);
  1890 + $len = strlen($code);
  1891 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
  1892 + if ($notkix) {
  1893 + // table for checksum calculation (row,col)
  1894 + $checktable = array(
  1895 + '0' => array(1, 1),
  1896 + '1' => array(1, 2),
  1897 + '2' => array(1, 3),
  1898 + '3' => array(1, 4),
  1899 + '4' => array(1, 5),
  1900 + '5' => array(1, 0),
  1901 + '6' => array(2, 1),
  1902 + '7' => array(2, 2),
  1903 + '8' => array(2, 3),
  1904 + '9' => array(2, 4),
  1905 + 'A' => array(2, 5),
  1906 + 'B' => array(2, 0),
  1907 + 'C' => array(3, 1),
  1908 + 'D' => array(3, 2),
  1909 + 'E' => array(3, 3),
  1910 + 'F' => array(3, 4),
  1911 + 'G' => array(3, 5),
  1912 + 'H' => array(3, 0),
  1913 + 'I' => array(4, 1),
  1914 + 'J' => array(4, 2),
  1915 + 'K' => array(4, 3),
  1916 + 'L' => array(4, 4),
  1917 + 'M' => array(4, 5),
  1918 + 'N' => array(4, 0),
  1919 + 'O' => array(5, 1),
  1920 + 'P' => array(5, 2),
  1921 + 'Q' => array(5, 3),
  1922 + 'R' => array(5, 4),
  1923 + 'S' => array(5, 5),
  1924 + 'T' => array(5, 0),
  1925 + 'U' => array(0, 1),
  1926 + 'V' => array(0, 2),
  1927 + 'W' => array(0, 3),
  1928 + 'X' => array(0, 4),
  1929 + 'Y' => array(0, 5),
  1930 + 'Z' => array(0, 0)
  1931 + );
  1932 + $row = 0;
  1933 + $col = 0;
  1934 + for ($i = 0; $i < $len; ++$i) {
  1935 + $row += $checktable[$code[$i]][0];
  1936 + $col += $checktable[$code[$i]][1];
  1937 + }
  1938 + $row %= 6;
  1939 + $col %= 6;
  1940 + $chk = array_keys($checktable, array($row, $col));
  1941 + $code .= $chk[0];
  1942 + ++$len;
  1943 + }
  1944 + $k = 0;
  1945 + if ($notkix) {
  1946 + // start bar
  1947 + $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
  1948 + $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  1949 + $bararray['maxw'] += 2;
  1950 + }
  1951 + for ($i = 0; $i < $len; ++$i) {
  1952 + for ($j = 0; $j < 4; ++$j) {
  1953 + switch ($barmode[$code[$i]][$j]) {
  1954 + case 1: {
  1955 + $p = 0;
  1956 + $h = 2;
  1957 + break;
  1958 + }
  1959 + case 2: {
  1960 + $p = 0;
  1961 + $h = 3;
  1962 + break;
  1963 + }
  1964 + case 3: {
  1965 + $p = 1;
  1966 + $h = 1;
  1967 + break;
  1968 + }
  1969 + case 4: {
  1970 + $p = 1;
  1971 + $h = 2;
  1972 + break;
  1973 + }
  1974 + }
  1975 + $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
  1976 + $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  1977 + $bararray['maxw'] += 2;
  1978 + }
  1979 + }
  1980 + if ($notkix) {
  1981 + // stop bar
  1982 + $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 3, 'p' => 0);
  1983 + $bararray['maxw'] += 1;
  1984 + }
  1985 +
  1986 + return $bararray;
  1987 + }
  1988 +
  1989 + /**
  1990 + * CODABAR barcodes.
  1991 + * Older code often used in library systems, sometimes in blood banks
  1992 + *
  1993 + * @param $code (string) code to represent.
  1994 + * @return array barcode representation.
  1995 + * @protected
  1996 + */
  1997 + protected function barcode_codabar($code)
  1998 + {
  1999 + $chr = array(
  2000 + '0' => '11111221',
  2001 + '1' => '11112211',
  2002 + '2' => '11121121',
  2003 + '3' => '22111111',
  2004 + '4' => '11211211',
  2005 + '5' => '21111211',
  2006 + '6' => '12111121',
  2007 + '7' => '12112111',
  2008 + '8' => '12211111',
  2009 + '9' => '21121111',
  2010 + '-' => '11122111',
  2011 + '$' => '11221111',
  2012 + ':' => '21112121',
  2013 + '/' => '21211121',
  2014 + '.' => '21212111',
  2015 + '+' => '11222221',
  2016 + 'A' => '11221211',
  2017 + 'B' => '12121121',
  2018 + 'C' => '11121221',
  2019 + 'D' => '11122211'
  2020 + );
  2021 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  2022 + $k = 0;
  2023 + $w = 0;
  2024 + $seq = '';
  2025 + $code = 'A' . strtoupper($code) . 'A';
  2026 + $len = strlen($code);
  2027 + for ($i = 0; $i < $len; ++$i) {
  2028 + if ( ! isset($chr[$code[$i]])) {
  2029 + throw new InvalidCharacterException('Char ' . $code[$i] . ' is unsupported');
  2030 + }
  2031 + $seq = $chr[$code[$i]];
  2032 + for ($j = 0; $j < 8; ++$j) {
  2033 + if (($j % 2) == 0) {
  2034 + $t = true; // bar
  2035 + } else {
  2036 + $t = false; // space
  2037 + }
  2038 + $w = $seq[$i];
  2039 + $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  2040 + $bararray['maxw'] += $w;
  2041 + ++$k;
  2042 + }
  2043 + }
  2044 +
  2045 + return $bararray;
  2046 + }
  2047 +
  2048 + /**
  2049 + * CODE11 barcodes.
  2050 + * Used primarily for labeling telecommunications equipment
  2051 + *
  2052 + * @param $code (string) code to represent.
  2053 + * @return array barcode representation.
  2054 + * @protected
  2055 + */
  2056 + protected function barcode_code11($code)
  2057 + {
  2058 + $chr = array(
  2059 + '0' => '111121',
  2060 + '1' => '211121',
  2061 + '2' => '121121',
  2062 + '3' => '221111',
  2063 + '4' => '112121',
  2064 + '5' => '212111',
  2065 + '6' => '122111',
  2066 + '7' => '111221',
  2067 + '8' => '211211',
  2068 + '9' => '211111',
  2069 + '-' => '112111',
  2070 + 'S' => '112211'
  2071 + );
  2072 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  2073 + $k = 0;
  2074 + $w = 0;
  2075 + $seq = '';
  2076 + $len = strlen($code);
  2077 + // calculate check digit C
  2078 + $p = 1;
  2079 + $check = 0;
  2080 + for ($i = ($len - 1); $i >= 0; --$i) {
  2081 + $digit = $code[$i];
  2082 + if ($digit == '-') {
  2083 + $dval = 10;
  2084 + } else {
  2085 + $dval = intval($digit);
  2086 + }
  2087 + $check += ($dval * $p);
  2088 + ++$p;
  2089 + if ($p > 10) {
  2090 + $p = 1;
  2091 + }
  2092 + }
  2093 + $check %= 11;
  2094 + if ($check == 10) {
  2095 + $check = '-';
  2096 + }
  2097 + $code .= $check;
  2098 + if ($len > 10) {
  2099 + // calculate check digit K
  2100 + $p = 1;
  2101 + $check = 0;
  2102 + for ($i = $len; $i >= 0; --$i) {
  2103 + $digit = $code[$i];
  2104 + if ($digit == '-') {
  2105 + $dval = 10;
  2106 + } else {
  2107 + $dval = intval($digit);
  2108 + }
  2109 + $check += ($dval * $p);
  2110 + ++$p;
  2111 + if ($p > 9) {
  2112 + $p = 1;
  2113 + }
  2114 + }
  2115 + $check %= 11;
  2116 + $code .= $check;
  2117 + ++$len;
  2118 + }
  2119 + $code = 'S' . $code . 'S';
  2120 + $len += 3;
  2121 + for ($i = 0; $i < $len; ++$i) {
  2122 + if ( ! isset($chr[$code[$i]])) {
  2123 + throw new InvalidCharacterException('Char ' . $code[$i] . ' is unsupported');
  2124 + }
  2125 + $seq = $chr[$code[$i]];
  2126 + for ($j = 0; $j < 6; ++$j) {
  2127 + if (($j % 2) == 0) {
  2128 + $t = true; // bar
  2129 + } else {
  2130 + $t = false; // space
  2131 + }
  2132 + $w = $seq[$j];
  2133 + $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  2134 + $bararray['maxw'] += $w;
  2135 + ++$k;
  2136 + }
  2137 + }
  2138 +
  2139 + return $bararray;
  2140 + }
  2141 +
  2142 + /**
  2143 + * Pharmacode
  2144 + * Contains digits (0 to 9)
  2145 + *
  2146 + * @param $code (string) code to represent.
  2147 + * @return array barcode representation.
  2148 + * @protected
  2149 + */
  2150 + protected function barcode_pharmacode($code)
  2151 + {
  2152 + $seq = '';
  2153 + $code = intval($code);
  2154 + while ($code > 0) {
  2155 + if (($code % 2) == 0) {
  2156 + $seq .= '11100';
  2157 + $code -= 2;
  2158 + } else {
  2159 + $seq .= '100';
  2160 + $code -= 1;
  2161 + }
  2162 + $code /= 2;
  2163 + }
  2164 + $seq = substr($seq, 0, -2);
  2165 + $seq = strrev($seq);
  2166 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  2167 +
  2168 + return $this->binseq_to_array($seq, $bararray);
  2169 + }
  2170 +
  2171 + /**
  2172 + * Pharmacode two-track
  2173 + * Contains digits (0 to 9)
  2174 + *
  2175 + * @param $code (string) code to represent.
  2176 + * @return array barcode representation.
  2177 + * @protected
  2178 + */
  2179 + protected function barcode_pharmacode2t($code)
  2180 + {
  2181 + $seq = '';
  2182 + $code = intval($code);
  2183 + do {
  2184 + switch ($code % 3) {
  2185 + case 0: {
  2186 + $seq .= '3';
  2187 + $code = ($code - 3) / 3;
  2188 + break;
  2189 + }
  2190 + case 1: {
  2191 + $seq .= '1';
  2192 + $code = ($code - 1) / 3;
  2193 + break;
  2194 + }
  2195 + case 2: {
  2196 + $seq .= '2';
  2197 + $code = ($code - 2) / 3;
  2198 + break;
  2199 + }
  2200 + }
  2201 + } while ($code != 0);
  2202 + $seq = strrev($seq);
  2203 + $k = 0;
  2204 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array());
  2205 + $len = strlen($seq);
  2206 + for ($i = 0; $i < $len; ++$i) {
  2207 + switch ($seq[$i]) {
  2208 + case '1': {
  2209 + $p = 1;
  2210 + $h = 1;
  2211 + break;
  2212 + }
  2213 + case '2': {
  2214 + $p = 0;
  2215 + $h = 1;
  2216 + break;
  2217 + }
  2218 + case '3': {
  2219 + $p = 0;
  2220 + $h = 2;
  2221 + break;
  2222 + }
  2223 + }
  2224 + $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
  2225 + $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  2226 + $bararray['maxw'] += 2;
  2227 + }
  2228 + unset($bararray['bcode'][($k - 1)]);
  2229 + --$bararray['maxw'];
  2230 +
  2231 + return $bararray;
  2232 + }
  2233 +
  2234 + /**
  2235 + * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
  2236 + * (requires PHP bcmath extension)
  2237 + * Intelligent Mail barcode is a 65-bar code for use on mail in the United States.
  2238 + * The fields are described as follows:<ul><li>The Barcode Identifier shall be assigned by USPS to encode the
  2239 + * presort identification that is currently printed in human readable form on the optional endorsement line (OEL)
  2240 + * as well as for future USPS use. This shall be two digits, with the second digit in the range of 0–4. The
  2241 + * allowable encoding ranges shall be 00–04, 10–14, 20–24, 30–34, 40–44, 50–54, 60–64, 70–74, 80–84, and
  2242 + * 90–94.</li><li>The Service Type Identifier shall be assigned by USPS for any combination of services requested
  2243 + * on the mailpiece. The allowable encoding range shall be 000http://it2.php.net/manual/en/function.dechex.php–999.
  2244 + * Each 3-digit value shall correspond to a particular mail class with a particular combination of service(s). Each
  2245 + * service program, such as OneCode Confirm and OneCode ACS, shall provide the list of Service Type Identifier
  2246 + * values.</li><li>The Mailer or Customer Identifier shall be assigned by USPS as a unique, 6 or 9 digit number
  2247 + * that identifies a business entity. The allowable encoding range for the 6 digit Mailer ID shall be 000000-
  2248 + * 899999, while the allowable encoding range for the 9 digit Mailer ID shall be 900000000-999999999.</li><li>The
  2249 + * Serial or Sequence Number shall be assigned by the mailer for uniquely identifying and tracking mailpieces. The
  2250 + * allowable encoding range shall be 000000000–999999999 when used with a 6 digit Mailer ID and 000000-999999 when
  2251 + * used with a 9 digit Mailer ID. e. The Delivery Point ZIP Code shall be assigned by the mailer for routing the
  2252 + * mailpiece. This shall replace POSTNET for routing the mailpiece to its final delivery point. The length may be
  2253 + * 0, 5, 9, or 11 digits. The allowable encoding ranges shall be no ZIP Code, 00000–99999, 000000000–999999999,
  2254 + * and 00000000000–99999999999.</li></ul>
  2255 + *
  2256 + * @param $code (string) code to print, separate the ZIP (routing code) from the rest using a minus char '-'
  2257 + * (BarcodeID_ServiceTypeID_MailerID_SerialNumber-RoutingCode)
  2258 + * @return array barcode representation.
  2259 + * @protected
  2260 + */
  2261 + protected function barcode_imb($code)
  2262 + {
  2263 + $asc_chr = array(
  2264 + 4,
  2265 + 0,
  2266 + 2,
  2267 + 6,
  2268 + 3,
  2269 + 5,
  2270 + 1,
  2271 + 9,
  2272 + 8,
  2273 + 7,
  2274 + 1,
  2275 + 2,
  2276 + 0,
  2277 + 6,
  2278 + 4,
  2279 + 8,
  2280 + 2,
  2281 + 9,
  2282 + 5,
  2283 + 3,
  2284 + 0,
  2285 + 1,
  2286 + 3,
  2287 + 7,
  2288 + 4,
  2289 + 6,
  2290 + 8,
  2291 + 9,
  2292 + 2,
  2293 + 0,
  2294 + 5,
  2295 + 1,
  2296 + 9,
  2297 + 4,
  2298 + 3,
  2299 + 8,
  2300 + 6,
  2301 + 7,
  2302 + 1,
  2303 + 2,
  2304 + 4,
  2305 + 3,
  2306 + 9,
  2307 + 5,
  2308 + 7,
  2309 + 8,
  2310 + 3,
  2311 + 0,
  2312 + 2,
  2313 + 1,
  2314 + 4,
  2315 + 0,
  2316 + 9,
  2317 + 1,
  2318 + 7,
  2319 + 0,
  2320 + 2,
  2321 + 4,
  2322 + 6,
  2323 + 3,
  2324 + 7,
  2325 + 1,
  2326 + 9,
  2327 + 5,
  2328 + 8
  2329 + );
  2330 + $dsc_chr = array(
  2331 + 7,
  2332 + 1,
  2333 + 9,
  2334 + 5,
  2335 + 8,
  2336 + 0,
  2337 + 2,
  2338 + 4,
  2339 + 6,
  2340 + 3,
  2341 + 5,
  2342 + 8,
  2343 + 9,
  2344 + 7,
  2345 + 3,
  2346 + 0,
  2347 + 6,
  2348 + 1,
  2349 + 7,
  2350 + 4,
  2351 + 6,
  2352 + 8,
  2353 + 9,
  2354 + 2,
  2355 + 5,
  2356 + 1,
  2357 + 7,
  2358 + 5,
  2359 + 4,
  2360 + 3,
  2361 + 8,
  2362 + 7,
  2363 + 6,
  2364 + 0,
  2365 + 2,
  2366 + 5,
  2367 + 4,
  2368 + 9,
  2369 + 3,
  2370 + 0,
  2371 + 1,
  2372 + 6,
  2373 + 8,
  2374 + 2,
  2375 + 0,
  2376 + 4,
  2377 + 5,
  2378 + 9,
  2379 + 6,
  2380 + 7,
  2381 + 5,
  2382 + 2,
  2383 + 6,
  2384 + 3,
  2385 + 8,
  2386 + 5,
  2387 + 1,
  2388 + 9,
  2389 + 8,
  2390 + 7,
  2391 + 4,
  2392 + 0,
  2393 + 2,
  2394 + 6,
  2395 + 3
  2396 + );
  2397 + $asc_pos = array(
  2398 + 3,
  2399 + 0,
  2400 + 8,
  2401 + 11,
  2402 + 1,
  2403 + 12,
  2404 + 8,
  2405 + 11,
  2406 + 10,
  2407 + 6,
  2408 + 4,
  2409 + 12,
  2410 + 2,
  2411 + 7,
  2412 + 9,
  2413 + 6,
  2414 + 7,
  2415 + 9,
  2416 + 2,
  2417 + 8,
  2418 + 4,
  2419 + 0,
  2420 + 12,
  2421 + 7,
  2422 + 10,
  2423 + 9,
  2424 + 0,
  2425 + 7,
  2426 + 10,
  2427 + 5,
  2428 + 7,
  2429 + 9,
  2430 + 6,
  2431 + 8,
  2432 + 2,
  2433 + 12,
  2434 + 1,
  2435 + 4,
  2436 + 2,
  2437 + 0,
  2438 + 1,
  2439 + 5,
  2440 + 4,
  2441 + 6,
  2442 + 12,
  2443 + 1,
  2444 + 0,
  2445 + 9,
  2446 + 4,
  2447 + 7,
  2448 + 5,
  2449 + 10,
  2450 + 2,
  2451 + 6,
  2452 + 9,
  2453 + 11,
  2454 + 2,
  2455 + 12,
  2456 + 6,
  2457 + 7,
  2458 + 5,
  2459 + 11,
  2460 + 0,
  2461 + 3,
  2462 + 2
  2463 + );
  2464 + $dsc_pos = array(
  2465 + 2,
  2466 + 10,
  2467 + 12,
  2468 + 5,
  2469 + 9,
  2470 + 1,
  2471 + 5,
  2472 + 4,
  2473 + 3,
  2474 + 9,
  2475 + 11,
  2476 + 5,
  2477 + 10,
  2478 + 1,
  2479 + 6,
  2480 + 3,
  2481 + 4,
  2482 + 1,
  2483 + 10,
  2484 + 0,
  2485 + 2,
  2486 + 11,
  2487 + 8,
  2488 + 6,
  2489 + 1,
  2490 + 12,
  2491 + 3,
  2492 + 8,
  2493 + 6,
  2494 + 4,
  2495 + 4,
  2496 + 11,
  2497 + 0,
  2498 + 6,
  2499 + 1,
  2500 + 9,
  2501 + 11,
  2502 + 5,
  2503 + 3,
  2504 + 7,
  2505 + 3,
  2506 + 10,
  2507 + 7,
  2508 + 11,
  2509 + 8,
  2510 + 2,
  2511 + 10,
  2512 + 3,
  2513 + 5,
  2514 + 8,
  2515 + 0,
  2516 + 3,
  2517 + 12,
  2518 + 11,
  2519 + 8,
  2520 + 4,
  2521 + 5,
  2522 + 1,
  2523 + 3,
  2524 + 0,
  2525 + 7,
  2526 + 12,
  2527 + 9,
  2528 + 8,
  2529 + 10
  2530 + );
  2531 + $code_arr = explode('-', $code);
  2532 + $tracking_number = $code_arr[0];
  2533 + if (isset($code_arr[1])) {
  2534 + $routing_code = $code_arr[1];
  2535 + } else {
  2536 + $routing_code = '';
  2537 + }
  2538 + // Conversion of Routing Code
  2539 + switch (strlen($routing_code)) {
  2540 + case 0: {
  2541 + $binary_code = 0;
  2542 + break;
  2543 + }
  2544 + case 5: {
  2545 + $binary_code = bcadd($routing_code, '1');
  2546 + break;
  2547 + }
  2548 + case 9: {
  2549 + $binary_code = bcadd($routing_code, '100001');
  2550 + break;
  2551 + }
  2552 + case 11: {
  2553 + $binary_code = bcadd($routing_code, '1000100001');
  2554 + break;
  2555 + }
  2556 + default: {
  2557 + throw new BarcodeException('Routing code unknown');
  2558 + break;
  2559 + }
  2560 + }
  2561 + $binary_code = bcmul($binary_code, 10);
  2562 + $binary_code = bcadd($binary_code, $tracking_number[0]);
  2563 + $binary_code = bcmul($binary_code, 5);
  2564 + $binary_code = bcadd($binary_code, $tracking_number[1]);
  2565 + $binary_code .= substr($tracking_number, 2, 18);
  2566 + // convert to hexadecimal
  2567 + $binary_code = $this->dec_to_hex($binary_code);
  2568 + // pad to get 13 bytes
  2569 + $binary_code = str_pad($binary_code, 26, '0', STR_PAD_LEFT);
  2570 + // convert string to array of bytes
  2571 + $binary_code_arr = chunk_split($binary_code, 2, "\r");
  2572 + $binary_code_arr = substr($binary_code_arr, 0, -1);
  2573 + $binary_code_arr = explode("\r", $binary_code_arr);
  2574 + // calculate frame check sequence
  2575 + $fcs = $this->imb_crc11fcs($binary_code_arr);
  2576 + // exclude first 2 bits from first byte
  2577 + $first_byte = sprintf('%2s', dechex((hexdec($binary_code_arr[0]) << 2) >> 2));
  2578 + $binary_code_102bit = $first_byte . substr($binary_code, 2);
  2579 + // convert binary data to codewords
  2580 + $codewords = array();
  2581 + $data = $this->hex_to_dec($binary_code_102bit);
  2582 + $codewords[0] = bcmod($data, 636) * 2;
  2583 + $data = bcdiv($data, 636);
  2584 + for ($i = 1; $i < 9; ++$i) {
  2585 + $codewords[$i] = bcmod($data, 1365);
  2586 + $data = bcdiv($data, 1365);
  2587 + }
  2588 + $codewords[9] = $data;
  2589 + if (($fcs >> 10) == 1) {
  2590 + $codewords[9] += 659;
  2591 + }
  2592 + // generate lookup tables
  2593 + $table2of13 = $this->imb_tables(2, 78);
  2594 + $table5of13 = $this->imb_tables(5, 1287);
  2595 + // convert codewords to characters
  2596 + $characters = array();
  2597 + $bitmask = 512;
  2598 + foreach ($codewords as $k => $val) {
  2599 + if ($val <= 1286) {
  2600 + $chrcode = $table5of13[$val];
  2601 + } else {
  2602 + $chrcode = $table2of13[($val - 1287)];
  2603 + }
  2604 + if (($fcs & $bitmask) > 0) {
  2605 + // bitwise invert
  2606 + $chrcode = ((~$chrcode) & 8191);
  2607 + }
  2608 + $characters[] = $chrcode;
  2609 + $bitmask /= 2;
  2610 + }
  2611 + $characters = array_reverse($characters);
  2612 + // build bars
  2613 + $k = 0;
  2614 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
  2615 + for ($i = 0; $i < 65; ++$i) {
  2616 + $asc = (($characters[$asc_chr[$i]] & pow(2, $asc_pos[$i])) > 0);
  2617 + $dsc = (($characters[$dsc_chr[$i]] & pow(2, $dsc_pos[$i])) > 0);
  2618 + if ($asc AND $dsc) {
  2619 + // full bar (F)
  2620 + $p = 0;
  2621 + $h = 3;
  2622 + } elseif ($asc) {
  2623 + // ascender (A)
  2624 + $p = 0;
  2625 + $h = 2;
  2626 + } elseif ($dsc) {
  2627 + // descender (D)
  2628 + $p = 1;
  2629 + $h = 2;
  2630 + } else {
  2631 + // tracker (T)
  2632 + $p = 1;
  2633 + $h = 1;
  2634 + }
  2635 + $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
  2636 + $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  2637 + $bararray['maxw'] += 2;
  2638 + }
  2639 + unset($bararray['bcode'][($k - 1)]);
  2640 + --$bararray['maxw'];
  2641 +
  2642 + return $bararray;
  2643 + }
  2644 +
  2645 + /**
  2646 + * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
  2647 + *
  2648 + * @param $code (string) pre-formatted IMB barcode (65 chars "FADT")
  2649 + * @return array barcode representation.
  2650 + * @protected
  2651 + */
  2652 + protected function barcode_imb_pre($code)
  2653 + {
  2654 + if ( ! preg_match('/^[fadtFADT]{65}$/', $code) == 1) {
  2655 + throw new InvalidFormatException();
  2656 + }
  2657 + $characters = str_split(strtolower($code), 1);
  2658 + // build bars
  2659 + $k = 0;
  2660 + $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
  2661 + for ($i = 0; $i < 65; ++$i) {
  2662 + switch ($characters[$i]) {
  2663 + case 'f': {
  2664 + // full bar
  2665 + $p = 0;
  2666 + $h = 3;
  2667 + break;
  2668 + }
  2669 + case 'a': {
  2670 + // ascender
  2671 + $p = 0;
  2672 + $h = 2;
  2673 + break;
  2674 + }
  2675 + case 'd': {
  2676 + // descender
  2677 + $p = 1;
  2678 + $h = 2;
  2679 + break;
  2680 + }
  2681 + case 't': {
  2682 + // tracker (short)
  2683 + $p = 1;
  2684 + $h = 1;
  2685 + break;
  2686 + }
  2687 + }
  2688 + $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
  2689 + $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  2690 + $bararray['maxw'] += 2;
  2691 + }
  2692 + unset($bararray['bcode'][($k - 1)]);
  2693 + --$bararray['maxw'];
  2694 +
  2695 + return $bararray;
  2696 + }
  2697 +
  2698 + /**
  2699 + * Convert large integer number to hexadecimal representation.
  2700 + * (requires PHP bcmath extension)
  2701 + *
  2702 + * @param $number (string) number to convert specified as a string
  2703 + * @return string hexadecimal representation
  2704 + */
  2705 + protected function dec_to_hex($number)
  2706 + {
  2707 + if ($number == 0) {
  2708 + return '00';
  2709 + }
  2710 +
  2711 + $hex = [];
  2712 +
  2713 + while ($number > 0) {
  2714 + array_push($hex, strtoupper(dechex(bcmod($number, '16'))));
  2715 + $number = bcdiv($number, '16', 0);
  2716 + }
  2717 + $hex = array_reverse($hex);
  2718 +
  2719 + return implode($hex);
  2720 + }
  2721 +
  2722 + /**
  2723 + * Convert large hexadecimal number to decimal representation (string).
  2724 + * (requires PHP bcmath extension)
  2725 + *
  2726 + * @param $hex (string) hexadecimal number to convert specified as a string
  2727 + * @return string hexadecimal representation
  2728 + */
  2729 + protected function hex_to_dec($hex)
  2730 + {
  2731 + $dec = 0;
  2732 + $bitval = 1;
  2733 + $len = strlen($hex);
  2734 + for ($pos = ($len - 1); $pos >= 0; --$pos) {
  2735 + $dec = bcadd($dec, bcmul(hexdec($hex[$pos]), $bitval));
  2736 + $bitval = bcmul($bitval, 16);
  2737 + }
  2738 +
  2739 + return $dec;
  2740 + }
  2741 +
  2742 + /**
  2743 + * Intelligent Mail Barcode calculation of Frame Check Sequence
  2744 + *
  2745 + * @param $code_arr (string) array of hexadecimal values (13 bytes holding 102 bits right justified).
  2746 + * @return int 11 bit Frame Check Sequence as integer (decimal base)
  2747 + * @protected
  2748 + */
  2749 + protected function imb_crc11fcs($code_arr)
  2750 + {
  2751 + $genpoly = 0x0F35; // generator polynomial
  2752 + $fcs = 0x07FF; // Frame Check Sequence
  2753 + // do most significant byte skipping the 2 most significant bits
  2754 + $data = hexdec($code_arr[0]) << 5;
  2755 + for ($bit = 2; $bit < 8; ++$bit) {
  2756 + if (($fcs ^ $data) & 0x400) {
  2757 + $fcs = ($fcs << 1) ^ $genpoly;
  2758 + } else {
  2759 + $fcs = ($fcs << 1);
  2760 + }
  2761 + $fcs &= 0x7FF;
  2762 + $data <<= 1;
  2763 + }
  2764 + // do rest of bytes
  2765 + for ($byte = 1; $byte < 13; ++$byte) {
  2766 + $data = hexdec($code_arr[$byte]) << 3;
  2767 + for ($bit = 0; $bit < 8; ++$bit) {
  2768 + if (($fcs ^ $data) & 0x400) {
  2769 + $fcs = ($fcs << 1) ^ $genpoly;
  2770 + } else {
  2771 + $fcs = ($fcs << 1);
  2772 + }
  2773 + $fcs &= 0x7FF;
  2774 + $data <<= 1;
  2775 + }
  2776 + }
  2777 +
  2778 + return $fcs;
  2779 + }
  2780 +
  2781 + /**
  2782 + * Reverse unsigned short value
  2783 + *
  2784 + * @param $num (int) value to reversr
  2785 + * @return int reversed value
  2786 + * @protected
  2787 + */
  2788 + protected function imb_reverse_us($num)
  2789 + {
  2790 + $rev = 0;
  2791 + for ($i = 0; $i < 16; ++$i) {
  2792 + $rev <<= 1;
  2793 + $rev |= ($num & 1);
  2794 + $num >>= 1;
  2795 + }
  2796 +
  2797 + return $rev;
  2798 + }
  2799 +
  2800 + /**
  2801 + * generate Nof13 tables used for Intelligent Mail Barcode
  2802 + *
  2803 + * @param $n (int) is the type of table: 2 for 2of13 table, 5 for 5of13table
  2804 + * @param $size (int) size of table (78 for n=2 and 1287 for n=5)
  2805 + * @return array requested table
  2806 + * @protected
  2807 + */
  2808 + protected function imb_tables($n, $size)
  2809 + {
  2810 + $table = array();
  2811 + $lli = 0; // LUT lower index
  2812 + $lui = $size - 1; // LUT upper index
  2813 + for ($count = 0; $count < 8192; ++$count) {
  2814 + $bit_count = 0;
  2815 + for ($bit_index = 0; $bit_index < 13; ++$bit_index) {
  2816 + $bit_count += intval(($count & (1 << $bit_index)) != 0);
  2817 + }
  2818 + // if we don't have the right number of bits on, go on to the next value
  2819 + if ($bit_count == $n) {
  2820 + $reverse = ($this->imb_reverse_us($count) >> 3);
  2821 + // if the reverse is less than count, we have already visited this pair before
  2822 + if ($reverse >= $count) {
  2823 + // If count is symmetric, place it at the first free slot from the end of the list.
  2824 + // Otherwise, place it at the first free slot from the beginning of the list AND place $reverse ath the next free slot from the beginning of the list
  2825 + if ($reverse == $count) {
  2826 + $table[$lui] = $count;
  2827 + --$lui;
  2828 + } else {
  2829 + $table[$lli] = $count;
  2830 + ++$lli;
  2831 + $table[$lli] = $reverse;
  2832 + ++$lli;
  2833 + }
  2834 + }
  2835 + }
  2836 + }
  2837 +
  2838 + return $table;
  2839 + }
  2840 +
  2841 + protected function convertBarcodeArrayToNewStyle($oldBarcodeArray)
  2842 + {
  2843 + $newBarcodeArray = [];
  2844 + $newBarcodeArray['code'] = $oldBarcodeArray['code'];
  2845 + $newBarcodeArray['maxWidth'] = $oldBarcodeArray['maxw'];
  2846 + $newBarcodeArray['maxHeight'] = $oldBarcodeArray['maxh'];
  2847 + $newBarcodeArray['bars'] = [];
  2848 + foreach ($oldBarcodeArray['bcode'] as $oldbar) {
  2849 + $newBar = [];
  2850 + $newBar['width'] = $oldbar['w'];
  2851 + $newBar['height'] = $oldbar['h'];
  2852 + $newBar['positionVertical'] = $oldbar['p'];
  2853 + $newBar['drawBar'] = $oldbar['t'];
  2854 + $newBar['drawSpacing'] = ! $oldbar['t'];
  2855 +
  2856 + $newBarcodeArray['bars'][] = $newBar;
  2857 + }
  2858 +
  2859 + return $newBarcodeArray;
  2860 + }
  2861 +}
  1 +<?php
  2 +
  3 +namespace Picqer\Barcode;
  4 +
  5 +class BarcodeGeneratorHTML extends BarcodeGenerator
  6 +{
  7 +
  8 + /**
  9 + * Return an HTML representation of barcode.
  10 + *
  11 + * @param string $code code to print
  12 + * @param string $type type of barcode
  13 + * @param int $widthFactor Width of a single bar element in pixels.
  14 + * @param int $totalHeight Height of a single bar element in pixels.
  15 + * @param int|string $color Foreground color for bar elements (background is transparent).
  16 + * @return string HTML code.
  17 + * @public
  18 + */
  19 + public function getBarcode($code, $type, $widthFactor = 2, $totalHeight = 30, $color = 'black')
  20 + {
  21 + $barcodeData = $this->getBarcodeData($code, $type);
  22 +
  23 + $html = '<div style="font-size:0;position:relative;width:' . ($barcodeData['maxWidth'] * $widthFactor) . 'px;height:' . ($totalHeight) . 'px;">' . "\n";
  24 +
  25 + $positionHorizontal = 0;
  26 + foreach ($barcodeData['bars'] as $bar) {
  27 + $barWidth = round(($bar['width'] * $widthFactor), 3);
  28 + $barHeight = round(($bar['height'] * $totalHeight / $barcodeData['maxHeight']), 3);
  29 +
  30 + if ($bar['drawBar']) {
  31 + $positionVertical = round(($bar['positionVertical'] * $totalHeight / $barcodeData['maxHeight']), 3);
  32 + // draw a vertical bar
  33 + $html .= '<div style="background-color:' . $color . ';width:' . $barWidth . 'px;height:' . $barHeight . 'px;position:absolute;left:' . $positionHorizontal . 'px;top:' . $positionVertical . 'px;">&nbsp;</div>' . "\n";
  34 + }
  35 +
  36 + $positionHorizontal += $barWidth;
  37 + }
  38 +
  39 + $html .= '</div>' . "\n";
  40 +
  41 + return $html;
  42 + }
  43 +}
  1 +<?php
  2 +
  3 +namespace Picqer\Barcode;
  4 +
  5 +use Picqer\Barcode\Exceptions\BarcodeException;
  6 +
  7 +class BarcodeGeneratorJPG extends BarcodeGenerator
  8 +{
  9 +
  10 + /**
  11 + * Return a JPG image representation of barcode (requires GD or Imagick library).
  12 + *
  13 + * @param string $code code to print
  14 + * @param string $type type of barcode:
  15 + * @param int $widthFactor Width of a single bar element in pixels.
  16 + * @param int $totalHeight Height of a single bar element in pixels.
  17 + * @param array $color RGB (0-255) foreground color for bar elements (background is transparent).
  18 + * @return string image data or false in case of error.
  19 + * @public
  20 + * @throws BarcodeException
  21 + */
  22 + public function getBarcode($code, $type, $widthFactor = 2, $totalHeight = 30, $color = array(0, 0, 0))
  23 + {
  24 + $barcodeData = $this->getBarcodeData($code, $type);
  25 +
  26 + // calculate image size
  27 + $width = ($barcodeData['maxWidth'] * $widthFactor);
  28 + $height = $totalHeight;
  29 +
  30 + if (function_exists('imagecreate')) {
  31 + // GD library
  32 + $imagick = false;
  33 + $jpg = imagecreate($width, $height);
  34 + $colorBackground = imagecolorallocate($jpg, 255, 255, 255);
  35 + imagecolortransparent($jpg, $colorBackground);
  36 + $colorForeground = imagecolorallocate($jpg, $color[0], $color[1], $color[2]);
  37 + } elseif (extension_loaded('imagick')) {
  38 + $imagick = true;
  39 + $colorForeground = new \imagickpixel('rgb(' . $color[0] . ',' . $color[1] . ',' . $color[2] . ')');
  40 + $jpg = new \Imagick();
  41 + $jpg->newImage($width, $height, 'white', 'jpg');
  42 + $imageMagickObject = new \imagickdraw();
  43 + $imageMagickObject->setFillColor($colorForeground);
  44 + } else {
  45 + throw new BarcodeException('Neither gd-lib or imagick are installed!');
  46 + }
  47 +
  48 + // print bars
  49 + $positionHorizontal = 0;
  50 + foreach ($barcodeData['bars'] as $bar) {
  51 + $bw = round(($bar['width'] * $widthFactor), 3);
  52 + $bh = round(($bar['height'] * $totalHeight / $barcodeData['maxHeight']), 3);
  53 + if ($bar['drawBar']) {
  54 + $y = round(($bar['positionVertical'] * $totalHeight / $barcodeData['maxHeight']), 3);
  55 + // draw a vertical bar
  56 + if ($imagick && isset($imageMagickObject)) {
  57 + $imageMagickObject->rectangle($positionHorizontal, $y, ($positionHorizontal + $bw), ($y + $bh));
  58 + } else {
  59 + imagefilledrectangle($jpg, $positionHorizontal, $y, ($positionHorizontal + $bw) - 1, ($y + $bh),
  60 + $colorForeground);
  61 + }
  62 + }
  63 + $positionHorizontal += $bw;
  64 + }
  65 + ob_start();
  66 + if ($imagick && isset($imageMagickObject)) {
  67 + $jpg->drawImage($imageMagickObject);
  68 + echo $jpg;
  69 + } else {
  70 + imagejpeg($jpg);
  71 + imagedestroy($jpg);
  72 + }
  73 + $image = ob_get_clean();
  74 +
  75 + return $image;
  76 + }
  77 +}
  1 +<?php
  2 +
  3 +namespace Picqer\Barcode;
  4 +
  5 +use Picqer\Barcode\Exceptions\BarcodeException;
  6 +
  7 +class BarcodeGeneratorPNG extends BarcodeGenerator
  8 +{
  9 +
  10 + /**
  11 + * Return a PNG image representation of barcode (requires GD or Imagick library).
  12 + *
  13 + * @param string $code code to print
  14 + * @param string $type type of barcode:
  15 + * @param int $widthFactor Width of a single bar element in pixels.
  16 + * @param int $totalHeight Height of a single bar element in pixels.
  17 + * @param array $color RGB (0-255) foreground color for bar elements (background is transparent).
  18 + * @return string image data or false in case of error.
  19 + * @public
  20 + */
  21 + public function getBarcode($code, $type, $widthFactor = 2, $totalHeight = 30, $color = array(0, 0, 0))
  22 + {
  23 + $barcodeData = $this->getBarcodeData($code, $type);
  24 +
  25 + // calculate image size
  26 + $width = ($barcodeData['maxWidth'] * $widthFactor);
  27 + $height = $totalHeight;
  28 +
  29 + if (function_exists('imagecreate')) {
  30 + // GD library
  31 + $imagick = false;
  32 + $png = imagecreate($width, $height);
  33 + $colorBackground = imagecolorallocate($png, 255, 255, 255);
  34 + imagecolortransparent($png, $colorBackground);
  35 + $colorForeground = imagecolorallocate($png, $color[0], $color[1], $color[2]);
  36 + } elseif (extension_loaded('imagick')) {
  37 + $imagick = true;
  38 + $colorForeground = new \imagickpixel('rgb(' . $color[0] . ',' . $color[1] . ',' . $color[2] . ')');
  39 + $png = new \Imagick();
  40 + $png->newImage($width, $height, 'none', 'png');
  41 + $imageMagickObject = new \imagickdraw();
  42 + $imageMagickObject->setFillColor($colorForeground);
  43 + } else {
  44 + throw new BarcodeException('Neither gd-lib or imagick are installed!');
  45 + }
  46 +
  47 + // print bars
  48 + $positionHorizontal = 0;
  49 + foreach ($barcodeData['bars'] as $bar) {
  50 + $bw = round(($bar['width'] * $widthFactor), 3);
  51 + $bh = round(($bar['height'] * $totalHeight / $barcodeData['maxHeight']), 3);
  52 + if ($bar['drawBar']) {
  53 + $y = round(($bar['positionVertical'] * $totalHeight / $barcodeData['maxHeight']), 3);
  54 + // draw a vertical bar
  55 + if ($imagick && isset($imageMagickObject)) {
  56 + $imageMagickObject->rectangle($positionHorizontal, $y, ($positionHorizontal + $bw), ($y + $bh));
  57 + } else {
  58 + imagefilledrectangle($png, $positionHorizontal, $y, ($positionHorizontal + $bw) - 1, ($y + $bh),
  59 + $colorForeground);
  60 + }
  61 + }
  62 + $positionHorizontal += $bw;
  63 + }
  64 + ob_start();
  65 + if ($imagick && isset($imageMagickObject)) {
  66 + $png->drawImage($imageMagickObject);
  67 + echo $png;
  68 + } else {
  69 + imagepng($png);
  70 + imagedestroy($png);
  71 + }
  72 + $image = ob_get_clean();
  73 +
  74 + return $image;
  75 + }
  76 +}
  1 +<?php
  2 +
  3 +namespace Picqer\Barcode;
  4 +
  5 +class BarcodeGeneratorSVG extends BarcodeGenerator
  6 +{
  7 +
  8 + /**
  9 + * Return a SVG string representation of barcode.
  10 + *
  11 + * @param $code (string) code to print
  12 + * @param $type (const) type of barcode
  13 + * @param $widthFactor (int) Minimum width of a single bar in user units.
  14 + * @param $totalHeight (int) Height of barcode in user units.
  15 + * @param $color (string) Foreground color (in SVG format) for bar elements (background is transparent).
  16 + * @return string SVG code.
  17 + * @public
  18 + */
  19 + public function getBarcode($code, $type, $widthFactor = 2, $totalHeight = 30, $color = 'black')
  20 + {
  21 + $barcodeData = $this->getBarcodeData($code, $type);
  22 +
  23 + // replace table for special characters
  24 + $repstr = array("\0" => '', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;');
  25 +
  26 + $width = round(($barcodeData['maxWidth'] * $widthFactor), 3);
  27 +
  28 + $svg = '<?xml version="1.0" standalone="no" ?>' . "\n";
  29 + $svg .= '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' . "\n";
  30 + $svg .= '<svg width="' . $width . '" height="' . $totalHeight . '" viewBox="0 0 ' . $width . ' ' . $totalHeight . '" version="1.1" xmlns="http://www.w3.org/2000/svg">' . "\n";
  31 + $svg .= "\t" . '<desc>' . strtr($barcodeData['code'], $repstr) . '</desc>' . "\n";
  32 + $svg .= "\t" . '<g id="bars" fill="' . $color . '" stroke="none">' . "\n";
  33 + // print bars
  34 + $positionHorizontal = 0;
  35 + foreach ($barcodeData['bars'] as $bar) {
  36 + $barWidth = round(($bar['width'] * $widthFactor), 3);
  37 + $barHeight = round(($bar['height'] * $totalHeight / $barcodeData['maxHeight']), 3);
  38 + if ($bar['drawBar']) {
  39 + $positionVertical = round(($bar['positionVertical'] * $totalHeight / $barcodeData['maxHeight']), 3);
  40 + // draw a vertical bar
  41 + $svg .= "\t\t" . '<rect x="' . $positionHorizontal . '" y="' . $positionVertical . '" width="' . $barWidth . '" height="' . $barHeight . '" />' . "\n";
  42 + }
  43 + $positionHorizontal += $barWidth;
  44 + }
  45 + $svg .= "\t" . '</g>' . "\n";
  46 + $svg .= '</svg>' . "\n";
  47 +
  48 + return $svg;
  49 + }
  50 +}
  1 +<?php
  2 +
  3 +namespace Picqer\Barcode\Exceptions;
  4 +
  5 +class BarcodeException extends \Exception {}
  1 +<?php
  2 +
  3 +namespace Picqer\Barcode\Exceptions;
  4 +
  5 +class InvalidCharacterException extends BarcodeException {}
  1 +<?php
  2 +
  3 +namespace Picqer\Barcode\Exceptions;
  4 +
  5 +class InvalidCheckDigitException extends BarcodeException {}
  1 +<?php
  2 +
  3 +namespace Picqer\Barcode\Exceptions;
  4 +
  5 +class InvalidFormatException extends BarcodeException {}
  1 +<?php
  2 +
  3 +namespace Picqer\Barcode\Exceptions;
  4 +
  5 +class InvalidLengthException extends BarcodeException {}
  1 +<?php
  2 +
  3 +namespace Picqer\Barcode\Exceptions;
  4 +
  5 +class UnknownTypeException extends BarcodeException {}
  1 +<?php
  2 +
  3 +namespace addons\barcode\library;
  4 +
  5 +
  6 +class Service
  7 +{
  8 + public static function barcode($params)
  9 + {
  10 + $params = is_array($params) ? $params : [$params];
  11 + $params['text'] = isset($params['text']) ? $params['text'] : 'Hello world!';
  12 + $params['type'] = isset($params['type']) ? $params['type'] : 'C128';
  13 + $params['width'] = isset($params['width']) ? $params['width'] : 2;
  14 + $params['height'] = isset($params['height']) ? $params['height'] : 30;
  15 + $params['foreground'] = isset($params['foreground']) ? $params['foreground'] : "#000000";
  16 +
  17 + // 前景色
  18 + list($r, $g, $b) = sscanf($params['foreground'], "#%02x%02x%02x");
  19 + $foregroundcolor = [$r, $g, $b];
  20 +
  21 + // 创建实例
  22 + $generator = new \Picqer\Barcode\BarcodeGeneratorPNG();
  23 + $barcode = $generator->getBarcode($params['text'], $params['type'], $params['width'], $params['height'], $foregroundcolor);
  24 +
  25 + return $barcode;
  26 + }
  27 +}
  1 +<!DOCTYPE html>
  2 +<html>
  3 + <head>
  4 + <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  5 + <title>条码生成 - {$site.name}</title>
  6 + <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  7 +
  8 + <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
  9 + <!--[if lt IE 9]>
  10 + <script src="http://apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js"></script>
  11 + <script src="http://apps.bdimg.com/libs/respond.js/1.4.2/respond.js"></script>
  12 + <![endif]-->
  13 + </head>
  14 + <body>
  15 + <div class="container">
  16 + <h2>条码生成</h2>
  17 + <hr>
  18 + <div class="well">
  19 + <form action="" method="post">
  20 + <div class="row">
  21 + <div class="col-md-4">
  22 + <div class="form-group">
  23 + <label class="control-label">文本内容</label>
  24 + <input type="text" name="text" class="form-control" placeholder="" value="1234567890">
  25 + </div>
  26 + <div class="form-group">
  27 + <label class="control-label">条码类型</label>
  28 + <select name="type" class="form-control">
  29 + <option value="C128">C128</option>
  30 + <option value="C128A">C128A</option>
  31 + <option value="C128B">C128B</option>
  32 + <option value="C128C">C128C</option>
  33 + <option value="C39">C39</option>
  34 + <option value="C39+">C39+</option>
  35 + <option value="C39E">C39E</option>
  36 + <option value="C39E+">C39E+</option>
  37 + <option value="S25">S25</option>
  38 + <option value="I25">I25</option>
  39 + <option value="I25+">I25+</option>
  40 + <option value="EAN2">EAN2</option>
  41 + <option value="EAN5">EAN5</option>
  42 + <option value="EAN8">EAN8</option>
  43 + <option value="EAN13">EAN13</option>
  44 + <option value="UPCA">UPCA</option>
  45 + <option value="UPCE">UPCE</option>
  46 + <option value="MSI">MSI</option>
  47 + <option value="POSTNET">POSTNET</option>
  48 + <option value="PLANET">PLANET</option>
  49 + <option value="RMS4CC">RMS4CC</option>
  50 + <option value="KIX">KIX</option>
  51 + <option value="IMB">IMB</option>
  52 + <option value="CODABAR">CODABAR</option>
  53 + <option value="CODE11">CODE11</option>
  54 + <option value="PHARMA">PHARMA</option>
  55 + <option value="PHARMA2T">PHARMA2T</option>
  56 + </select>
  57 + </div>
  58 +
  59 + <div class="form-group">
  60 + <input type="submit" class="btn btn-info" />
  61 + <input type="reset" class="btn btn-default" />
  62 + </div>
  63 + </div>
  64 +
  65 + <div class="col-md-2">
  66 + <div class="form-group">
  67 + <div class="form-group">
  68 + <label class="control-label">前景色</label>
  69 + <input type="text" name="foreground" placeholder="" class="form-control" value="#000000">
  70 + </div>
  71 + <div class="form-group">
  72 + <label class="control-label">间距</label>
  73 + <input type="number" name="width" placeholder="" class="form-control" value="2">
  74 + </div>
  75 + <div class="form-group">
  76 + <label class="control-label">高度</label>
  77 + <input type="number" name="height" placeholder="" class="form-control" value="30">
  78 + </div>
  79 + </div>
  80 + </div>
  81 + </div>
  82 + </form>
  83 + </div>
  84 + <input type="text" class="form-control" id='barcodeurl' />
  85 + <img src="" alt="" id='barcodeimg' style="margin-top:15px;"/>
  86 + </div>
  87 + <script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
  88 + <script type="text/javascript">
  89 + $(function () {
  90 + $("form").submit(function () {
  91 + $("#barcodeimg").prop("src", "{:addon_url('barcode/index/build',[],false)}?" + $(this).serialize());
  92 + $("#barcodeurl").val("{:addon_url('barcode/index/build',[],false,true)}?" + $(this).serialize());
  93 + return false;
  94 + });
  95 + $("form").trigger('submit');
  96 + });
  97 + </script>
  98 + </body>
  99 +</html>
  1 +{"files":[],"license":"basic","licenseto":"10789","licensekey":"ZC8AptuI2eqxYS4l 0A2s088RGt+\/yZpmeu\/k8w==","domains":["campus.cn"],"licensecodes":[],"validations":["c51a74160ee0aa6a19729b33d4a9fcdc"]}
  1 +<?php
  2 +
  3 +namespace addons\qiniu;
  4 +
  5 +use fast\Http;
  6 +use Qiniu\Auth;
  7 +use think\Addons;
  8 +use think\App;
  9 +use think\Loader;
  10 +
  11 +/**
  12 + * 七牛云储存插件
  13 + */
  14 +class Qiniu extends Addons
  15 +{
  16 +
  17 + /**
  18 + * 插件安装方法
  19 + * @return bool
  20 + */
  21 + public function install()
  22 + {
  23 + return true;
  24 + }
  25 +
  26 + /**
  27 + * 插件卸载方法
  28 + * @return bool
  29 + */
  30 + public function uninstall()
  31 + {
  32 + return true;
  33 + }
  34 +
  35 + /**
  36 + * 判断是否来源于API上传
  37 + */
  38 + public function moduleInit($request)
  39 + {
  40 + $config = $this->getConfig();
  41 + $module = strtolower($request->module());
  42 + if ($module == 'api' && ($config['apiupload'] ?? 0) &&
  43 + strtolower($request->controller()) == 'common' &&
  44 + strtolower($request->action()) == 'upload') {
  45 + request()->param('isApi', true);
  46 + App::invokeMethod(["\\addons\\qiniu\\controller\\Index", "upload"], ['isApi' => true]);
  47 + }
  48 + }
  49 +
  50 + /**
  51 + * 上传初始化时
  52 + */
  53 + public function uploadConfigInit(&$upload)
  54 + {
  55 + $config = $this->getConfig();
  56 + $module = request()->module();
  57 + $module = $module ? strtolower($module) : 'index';
  58 +
  59 + $policy = array(
  60 + 'saveKey' => ltrim($config['savekey'], '/'),
  61 + );
  62 +
  63 + $config['savekey'] = str_replace(
  64 + ['$(year)', '$(mon)', '$(day)', '$(hour)', '$(min)', '$(sec)', '$(etag)', '$(ext)', '$(fname)'],
  65 + ['{year}', '{mon}', '{day}', '$(hour)', '$(min)', '$(sec)', '{filemd5}', '{.suffix}', '{filename}'],
  66 + $config['savekey']
  67 + );
  68 + $auth = new Auth($config['accessKey'], $config['secretKey']);
  69 +
  70 + $token = '';
  71 + $noNeedLogin = array_filter(explode(',', $config['noneedlogin'] ?? ''));
  72 + $isModuleLogin = false;
  73 + $tagName = 'upload_config_checklogin';
  74 + foreach (\think\Hook::get($tagName) as $index => $name) {
  75 + if (\think\Hook::exec($name, $tagName)) {
  76 + $isModuleLogin = true;
  77 + break;
  78 + }
  79 + }
  80 + if (in_array($module, $noNeedLogin)
  81 + || ($module == 'admin' && \app\admin\library\Auth::instance()->id)
  82 + || ($module != 'admin' && \app\common\library\Auth::instance()->id)
  83 + || $isModuleLogin
  84 + ) {
  85 + $token = $auth->uploadToken($config['bucket'], null, $config['expire'], $policy);
  86 + }
  87 + $multipart = [
  88 + 'qiniutoken' => $token
  89 + ];
  90 +
  91 + $upload = array_merge($upload, [
  92 + 'cdnurl' => $config['cdnurl'],
  93 + 'uploadurl' => $config['uploadmode'] == 'client' ? $config['uploadurl'] : addon_url('qiniu/index/upload', [], false, true),
  94 + 'uploadmode' => $config['uploadmode'],
  95 + 'bucket' => $config['bucket'],
  96 + 'maxsize' => $config['maxsize'],
  97 + 'mimetype' => $config['mimetype'],
  98 + 'savekey' => $config['savekey'],
  99 + 'chunking' => (bool)($config['chunking'] ?? $upload['chunking']),
  100 + 'chunksize' => (int)($config['chunksize'] ?? $upload['chunksize']),
  101 + 'multipart' => $multipart,
  102 + 'storage' => $this->getName(),
  103 + 'multiple' => $config['multiple'] ? true : false,
  104 + ]);
  105 + }
  106 +
  107 + /**
  108 + * 附件删除后
  109 + */
  110 + public function uploadDelete($attachment)
  111 + {
  112 + $config = $this->getConfig();
  113 + if ($attachment['storage'] == 'qiniu' && isset($config['syncdelete']) && $config['syncdelete']) {
  114 + $auth = new Auth($config['accessKey'], $config['secretKey']);
  115 + $entry = $config['bucket'] . ':' . ltrim($attachment->url, '/');
  116 + $encodedEntryURI = \Qiniu\base64_urlSafeEncode($entry);
  117 + $url = 'http://rs.qiniu.com/delete/' . $encodedEntryURI;
  118 + $headers = $auth->authorization($url);
  119 + //删除云储存文件
  120 + $ret = Http::sendRequest($url, [], 'POST', [CURLOPT_HTTPHEADER => ['Authorization: ' . $headers['Authorization']]]);
  121 + //如果是服务端中转,还需要删除本地文件
  122 + //if ($config['uploadmode'] == 'server') {
  123 + // $filePath = ROOT_PATH . 'public' . str_replace('/', DS, $attachment->url);
  124 + // if ($filePath) {
  125 + // @unlink($filePath);
  126 + // }
  127 + //}
  128 + }
  129 + return true;
  130 + }
  131 +
  132 + public function appInit()
  133 + {
  134 + if (!class_exists('\Qiniu\Config')) {
  135 + Loader::addNamespace('Qiniu', ADDON_PATH . 'qiniu' . DS . 'library' . DS . 'Qiniu' . DS);
  136 + require_once ADDON_PATH . 'qiniu' . DS . 'library' . DS . 'Qiniu' . DS . 'functions.php';
  137 + }
  138 + }
  139 +
  140 +}
  1 +//修改上传的接口调用
  2 +require(['upload'], function (Upload) {
  3 +
  4 + var _onInit = Upload.events.onInit;
  5 + //初始化中完成判断
  6 + Upload.events.onInit = function () {
  7 + _onInit.apply(this, Array.prototype.slice.apply(arguments));
  8 + //如果上传接口不是七牛云,则不处理
  9 + if (this.options.url !== Config.upload.uploadurl) {
  10 + return;
  11 + }
  12 + var _success = this.options.success;
  13 +
  14 + $.extend(this.options, {
  15 + //关闭自动处理队列功能
  16 + autoQueue: false,
  17 + chunkSuccess: function (chunk, file, response) {
  18 + this.contexts = this.contexts ? this.contexts : [];
  19 + this.contexts.push(typeof response.ctx !== 'undefined' ? response.ctx : response.data.ctx);
  20 + },
  21 + chunksUploaded: function (file, done) {
  22 + var that = this;
  23 + var params = $(that.element).data("params") || {};
  24 + var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
  25 + category = typeof category === 'function' ? category.call(this, file) : category;
  26 + Fast.api.ajax({
  27 + url: "/addons/qiniu/index/upload",
  28 + data: {
  29 + action: 'merge',
  30 + filesize: file.size,
  31 + filename: file.name,
  32 + chunkid: file.upload.uuid,
  33 + chunkcount: file.upload.totalChunkCount,
  34 + width: file.width || 0,
  35 + height: file.height || 0,
  36 + type: file.type,
  37 + category: category,
  38 + qiniutoken: Config.upload.multipart.qiniutoken,
  39 + contexts: this.contexts
  40 + },
  41 + }, function (data, ret) {
  42 + done(JSON.stringify(ret));
  43 + return false;
  44 + }, function (data, ret) {
  45 + file.accepted = false;
  46 + that._errorProcessing([file], ret.msg);
  47 + return false;
  48 + });
  49 +
  50 + },
  51 + });
  52 +
  53 + //先移除已有的事件
  54 + this.off("success", _success).on("success", function (file, response) {
  55 + var that = this;
  56 + var ret = {code: 0, msg: response};
  57 + try {
  58 + ret = typeof response === 'string' ? JSON.parse(response) : response;
  59 + if (file.xhr.status === 200) {
  60 + if (Config.upload.uploadmode === 'client') {
  61 + if (typeof ret.key !== 'undefined') {
  62 + ret = {code: 1, msg: "", data: {url: '/' + ret.key, hash: ret.hash}};
  63 + }
  64 + }
  65 +
  66 + if (ret.code == 1) {
  67 + var url = ret.data.url || '';
  68 + var params = $(that.element).data("params") || {};
  69 + var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
  70 + category = typeof category === 'function' ? category.call(that, file) : category;
  71 + Fast.api.ajax({
  72 + url: "/addons/qiniu/index/notify",
  73 + data: {name: file.name, url: ret.data.url, hash: ret.data.hash, size: file.size, width: file.width || 0, height: file.height || 0, type: file.type, category: category, qiniutoken: Config.upload.multipart.qiniutoken}
  74 + }, function () {
  75 + return false;
  76 + }, function () {
  77 + return false;
  78 + });
  79 + } else {
  80 + console.error(ret);
  81 + }
  82 + } else {
  83 + console.error(file.xhr);
  84 + }
  85 + } catch (e) {
  86 + console.error(e);
  87 + }
  88 + _success.call(this, file, ret);
  89 + });
  90 +
  91 + this.on("addedfile", function (file) {
  92 + var that = this;
  93 + setTimeout(function () {
  94 + if (file.status === 'error') {
  95 + return;
  96 + }
  97 +
  98 + var md5 = ''; //七牛云无需本地获取文件MD5
  99 + var chunk = that.options.chunking && file.size > that.options.chunkSize ? 1 : 0;
  100 + var params = $(that.element).data("params") || {};
  101 + var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
  102 + category = typeof category === 'function' ? category.call(that, file) : category;
  103 + Fast.api.ajax({
  104 + url: "/addons/qiniu/index/params",
  105 + data: {method: 'POST', category: category, md5: md5, name: file.name, type: file.type, size: file.size, chunk: chunk, chunksize: that.options.chunkSize, qiniutoken: Config.upload.multipart.qiniutoken},
  106 + }, function (data) {
  107 + file.qiniutoken = data.qiniutoken;
  108 + file.params = data;
  109 + file.category = category;
  110 +
  111 + if (file.status != 'error') {
  112 + //开始上传
  113 + that.enqueueFile(file);
  114 + } else {
  115 + that.removeFile(file);
  116 + }
  117 + return false;
  118 + }, function () {
  119 + that.removeFile(file);
  120 + });
  121 + }, 0);
  122 + });
  123 +
  124 + //如果是直传模式
  125 + if (Config.upload.uploadmode === 'client') {
  126 + var _url = this.options.url;
  127 +
  128 + //分片上传时URL链接不同
  129 + this.options.url = function (files) {
  130 + this.options.headers = {"Authorization": "UpToken " + Config.upload.multipart.qiniutoken};
  131 + if (files[0].upload.chunked) {
  132 + var chunk = null;
  133 + files[0].upload.chunks.forEach(function (item) {
  134 + if (item.status === 'uploading') {
  135 + chunk = item;
  136 + }
  137 + });
  138 + if (!chunk) {
  139 + return Config.upload.uploadurl + '/mkfile/' + files[0].size;
  140 + } else {
  141 + return Config.upload.uploadurl + '/mkblk/' + chunk.dataBlock.data.size;
  142 + }
  143 + }
  144 + return _url;
  145 + };
  146 +
  147 + this.options.params = function (files, xhr, chunk) {
  148 + var params = Config.upload.multipart;
  149 + if (chunk) {
  150 + return $.extend({}, params, {
  151 + filesize: chunk.file.size,
  152 + filename: chunk.file.name,
  153 + chunkid: chunk.file.upload.uuid,
  154 + chunkindex: chunk.index,
  155 + chunkcount: chunk.file.upload.totalChunkCount,
  156 + chunkfilesize: chunk.dataBlock.data.size,
  157 + width: chunk.file.width || 0,
  158 + height: chunk.file.height || 0,
  159 + type: chunk.file.type,
  160 + });
  161 + } else {
  162 + var retParams = $.extend({}, params, files[0].params || {});
  163 + //七牛云直传使用的是token参数
  164 + retParams.token = retParams.qiniutoken;
  165 + delete retParams.qiniutoken;
  166 + return retParams;
  167 + }
  168 + };
  169 +
  170 + //分片上传时需要变更提交的内容
  171 + this.on("sending", function (file, xhr, formData) {
  172 + if (file.upload.chunked) {
  173 + var _send = xhr.send;
  174 + xhr.send = function () {
  175 + var chunk = null;
  176 + file.upload.chunks.forEach(function (item) {
  177 + if (item.status == 'uploading') {
  178 + chunk = item;
  179 + }
  180 + });
  181 + if (chunk) {
  182 + _send.call(xhr, chunk.dataBlock.data);
  183 + }
  184 + };
  185 + }
  186 + });
  187 + }
  188 + };
  189 +
  190 +});
  1 +<?php
  2 +
  3 +return [
  4 + [
  5 + 'name' => 'accessKey',
  6 + 'title' => 'accessKey',
  7 + 'type' => 'string',
  8 + 'content' => [],
  9 + 'value' => '8BbfcyH8uJI0gVa2XXjkp9FRImn3TlSRq7RW8L01',
  10 + 'rule' => 'required',
  11 + 'msg' => '',
  12 + 'tip' => '请在个人中心 > 密钥管理中获取 > AK',
  13 + 'ok' => '',
  14 + 'extend' => '',
  15 + ],
  16 + [
  17 + 'name' => 'secretKey',
  18 + 'title' => 'secretKey',
  19 + 'type' => 'string',
  20 + 'content' => [],
  21 + 'value' => 'cvcdMaQtIgJQ_K5X84DT01yrzylVYF84dvbva6Zi',
  22 + 'rule' => 'required',
  23 + 'msg' => '',
  24 + 'tip' => '请在个人中心 > 密钥管理中获取 > SK',
  25 + 'ok' => '',
  26 + 'extend' => '',
  27 + ],
  28 + [
  29 + 'name' => 'bucket',
  30 + 'title' => 'bucket',
  31 + 'type' => 'string',
  32 + 'content' => [],
  33 + 'value' => 'campus1',
  34 + 'rule' => 'required;bucket',
  35 + 'msg' => '',
  36 + 'tip' => '存储空间名称',
  37 + 'ok' => '',
  38 + 'extend' => 'data-rule-bucket="[/^[0-9a-z_\\-]{3,63}$/, \'请输入正确的Bucket名称\']"',
  39 + ],
  40 + [
  41 + 'name' => 'uploadurl',
  42 + 'title' => '上传接口地址',
  43 + 'type' => 'select',
  44 + 'content' => [
  45 + 'https://upload-z0.qiniup.com' => '华东 https://upload-z0.qiniup.com',
  46 + 'https://upload-z1.qiniup.com' => '华北 https://upload-z1.qiniup.com',
  47 + 'https://upload-z2.qiniup.com' => '华南 https://upload-z2.qiniup.com',
  48 + 'https://upload-na0.qiniup.com' => '北美 https://upload-na0.qiniup.com',
  49 + 'https://upload-as0.qiniup.com' => '东南亚 https://upload-as0.qiniup.com',
  50 + 'https://up-cn-east-2.qiniup.com' => '华东 浙江2 https://up-cn-east-2.qiniup.com',
  51 + ],
  52 + 'value' => 'https://upload-z1.qiniup.com',
  53 + 'rule' => 'required',
  54 + 'msg' => '',
  55 + 'tip' => '推荐选择最近的地址',
  56 + 'ok' => '',
  57 + 'extend' => '',
  58 + ],
  59 + [
  60 + 'name' => 'cdnurl',
  61 + 'title' => 'CDN地址',
  62 + 'type' => 'string',
  63 + 'content' => [],
  64 + 'value' => 'http://rramxwmk1.hb-bkt.clouddn.com',
  65 + 'rule' => 'required;cdnurl',
  66 + 'msg' => '',
  67 + 'tip' => '未绑定CDN的话可使用七牛分配的测试域名',
  68 + 'ok' => '',
  69 + 'extend' => 'data-rule-cdnurl="[/^http(s)?:\\/\\/.*$/, \'必需以http(s)://开头\']"',
  70 + ],
  71 + [
  72 + 'name' => 'uploadmode',
  73 + 'title' => '上传模式',
  74 + 'type' => 'select',
  75 + 'content' => [
  76 + 'client' => '客户端直传(速度快,无备份)',
  77 + 'server' => '服务器中转(占用服务器带宽,可备份)',
  78 + ],
  79 + 'value' => 'server',
  80 + 'rule' => '',
  81 + 'msg' => '',
  82 + 'tip' => '启用服务器中转时务必配置操作员和密码',
  83 + 'ok' => '',
  84 + 'extend' => '',
  85 + ],
  86 + [
  87 + 'name' => 'serverbackup',
  88 + 'title' => '服务器中转模式备份',
  89 + 'type' => 'radio',
  90 + 'content' => [
  91 + 1 => '备份(附件管理将产生2条记录)',
  92 + 0 => '不备份',
  93 + ],
  94 + 'value' => '1',
  95 + 'rule' => '',
  96 + 'msg' => '',
  97 + 'tip' => '服务器中转模式下是否备份文件',
  98 + 'ok' => '',
  99 + 'extend' => '',
  100 + ],
  101 + [
  102 + 'name' => 'savekey',
  103 + 'title' => '保存文件名',
  104 + 'type' => 'string',
  105 + 'content' => [],
  106 + 'value' => '/uploads/{year}{mon}{day}/{filemd5}{.suffix}',
  107 + 'rule' => 'required',
  108 + 'msg' => '',
  109 + 'tip' => '',
  110 + 'ok' => '',
  111 + 'extend' => '',
  112 + ],
  113 + [
  114 + 'name' => 'expire',
  115 + 'title' => '上传有效时长',
  116 + 'type' => 'string',
  117 + 'content' => [],
  118 + 'value' => '600',
  119 + 'rule' => 'required',
  120 + 'msg' => '',
  121 + 'tip' => '用户停留页面上传有效时长,单位秒',
  122 + 'ok' => '',
  123 + 'extend' => '',
  124 + ],
  125 + [
  126 + 'name' => 'maxsize',
  127 + 'title' => '最大可上传',
  128 + 'type' => 'string',
  129 + 'content' => [],
  130 + 'value' => '10G',
  131 + 'rule' => 'required',
  132 + 'msg' => '',
  133 + 'tip' => '',
  134 + 'ok' => '',
  135 + 'extend' => '',
  136 + ],
  137 + [
  138 + 'name' => 'mimetype',
  139 + 'title' => '可上传后缀格式',
  140 + 'type' => 'string',
  141 + 'content' => [],
  142 + 'value' => 'jpg,png,bmp,jpeg,gif,zip,rar,xls,xlsx',
  143 + 'rule' => 'required',
  144 + 'msg' => '',
  145 + 'tip' => '',
  146 + 'ok' => '',
  147 + 'extend' => '',
  148 + ],
  149 + [
  150 + 'name' => 'multiple',
  151 + 'title' => '多文件上传',
  152 + 'type' => 'radio',
  153 + 'content' => [
  154 + 1 => '开启',
  155 + 0 => '关闭',
  156 + ],
  157 + 'value' => '0',
  158 + 'rule' => 'required',
  159 + 'msg' => '',
  160 + 'tip' => '',
  161 + 'ok' => '',
  162 + 'extend' => '',
  163 + ],
  164 + [
  165 + 'name' => 'thumbstyle',
  166 + 'title' => '缩略图样式',
  167 + 'type' => 'string',
  168 + 'content' => [],
  169 + 'value' => '',
  170 + 'rule' => '',
  171 + 'msg' => '',
  172 + 'tip' => '用于后台列表缩略图样式,可使用:?imageView2/2/w/120/h/90/q/80',
  173 + 'ok' => '',
  174 + 'extend' => '',
  175 + ],
  176 + [
  177 + 'name' => 'chunking',
  178 + 'title' => '分片上传',
  179 + 'type' => 'radio',
  180 + 'content' => [
  181 + 1 => '开启',
  182 + 0 => '关闭',
  183 + ],
  184 + 'value' => '1',
  185 + 'rule' => 'required',
  186 + 'msg' => '',
  187 + 'tip' => '',
  188 + 'ok' => '',
  189 + 'extend' => '',
  190 + ],
  191 + [
  192 + 'name' => 'chunksize',
  193 + 'title' => '分片大小',
  194 + 'type' => 'number',
  195 + 'content' => [],
  196 + 'value' => '4194304',
  197 + 'rule' => 'required',
  198 + 'msg' => '',
  199 + 'tip' => '固定大小,不能修改',
  200 + 'ok' => '',
  201 + 'extend' => 'readonly',
  202 + ],
  203 + [
  204 + 'name' => 'syncdelete',
  205 + 'title' => '附件删除时是否同步删除文件',
  206 + 'type' => 'bool',
  207 + 'content' => [],
  208 + 'value' => '0',
  209 + 'rule' => 'required',
  210 + 'msg' => '',
  211 + 'tip' => '',
  212 + 'ok' => '',
  213 + 'extend' => '',
  214 + ],
  215 + [
  216 + 'name' => 'apiupload',
  217 + 'title' => 'API接口使用云存储',
  218 + 'type' => 'bool',
  219 + 'content' => [],
  220 + 'value' => '0',
  221 + 'rule' => 'required',
  222 + 'msg' => '',
  223 + 'tip' => '',
  224 + 'ok' => '',
  225 + 'extend' => '',
  226 + ],
  227 + [
  228 + 'name' => 'noneedlogin',
  229 + 'title' => '免登录上传',
  230 + 'type' => 'checkbox',
  231 + 'content' => [
  232 + 'api' => 'API',
  233 + 'index' => '前台',
  234 + 'admin' => '后台',
  235 + ],
  236 + 'value' => '',
  237 + 'rule' => '',
  238 + 'msg' => '',
  239 + 'tip' => '',
  240 + 'ok' => '',
  241 + 'extend' => '',
  242 + ],
  243 +];
  1 +<?php
  2 +
  3 +namespace addons\qiniu\controller;
  4 +
  5 +use app\common\exception\UploadException;
  6 +use app\common\library\Upload;
  7 +use app\common\model\Attachment;
  8 +use fast\Random;
  9 +use Qiniu\Auth;
  10 +use Qiniu\Storage\ResumeUploader;
  11 +use Qiniu\Storage\UploadManager;
  12 +use think\addons\Controller;
  13 +use think\Config;
  14 +
  15 +/**
  16 + * 七牛管理
  17 + *
  18 + */
  19 +class Index extends Controller
  20 +{
  21 +
  22 + public function _initialize()
  23 + {
  24 + //跨域检测
  25 + check_cors_request();
  26 +
  27 + parent::_initialize();
  28 + Config::set('default_return_type', 'json');
  29 + }
  30 +
  31 + public function index()
  32 + {
  33 + Config::set('default_return_type', 'html');
  34 + $this->error("当前插件暂无前台页面");
  35 + }
  36 +
  37 + /**
  38 + * 获取签名
  39 + */
  40 + public function params()
  41 + {
  42 + $this->check();
  43 + $name = $this->request->post('name');
  44 + $md5 = $this->request->post('md5');
  45 + $chunk = $this->request->post('chunk');
  46 +
  47 + $config = get_addon_config('qiniu');
  48 +
  49 + $name = xss_clean($name);
  50 + $config['savekey'] = (new Upload())->getSavekey($config['savekey'], $name, '$(etag)');
  51 +
  52 + // 构建鉴权对象
  53 + $auth = new Auth($config['accessKey'], $config['secretKey']);
  54 + // 生成上传 Token
  55 + $token = $auth->uploadToken($config['bucket'], null, 3600, ['saveKey' => ltrim($config['savekey'], '/')]);
  56 +
  57 + $params['qiniutoken'] = $token;
  58 +
  59 + $this->success('', null, $params);
  60 + return;
  61 + }
  62 +
  63 + /**
  64 + * 中转上传文件
  65 + * 上传分片
  66 + * 合并分片
  67 + * @param bool $isApi
  68 + * @throws \Exception
  69 + */
  70 + public function upload($isApi = false)
  71 + {
  72 + Config::set('default_return_type', 'json');
  73 +
  74 + if ($isApi === true) {
  75 + if (!$this->auth->isLogin()) {
  76 + $this->error("请登录后再进行操作");
  77 + }
  78 + } else {
  79 + $this->check();
  80 + }
  81 +
  82 + $config = get_addon_config('qiniu');
  83 + $config['savekey'] = str_replace(
  84 + ['$(year)', '$(mon)', '$(day)', '$(hour)', '$(min)', '$(sec)', '$(etag)', '$(ext)', '$(fname)', '$(fprefix)'],
  85 + ['{year}', '{mon}', '{day}', '{hour}', '{min}', '{sec}', '{filemd5}', '{.suffix}', '{filename}', '{fileprefix}'],
  86 + $config['savekey']
  87 + );
  88 + $file = $this->request->file('file');
  89 +
  90 + $chunkid = $this->request->post("chunkid");
  91 + $md5 = $chunkid && $this->request->post("action") == 'merge' ? md5(str_replace('-', '', $chunkid)) : null;
  92 + $config['savekey'] = (new Upload($file))->getSavekey($config['savekey'], null, $md5);
  93 +
  94 + // 构建鉴权对象
  95 + $auth = new Auth($config['accessKey'], $config['secretKey']);
  96 + // 生成上传 Token
  97 + $token = $auth->uploadToken($config['bucket'], null, 3600, ['saveKey' => ltrim($config['savekey'], '/')]);
  98 + // 初始化 UploadManager 对象并进行文件的上传。
  99 + $uploadMgr = new UploadManager();
  100 +
  101 + //检测删除文件或附件
  102 + $checkDeleteFile = function ($attachment, $upload, $force = false) use ($config) {
  103 + //如果设定为不备份则删除文件和记录 或 强制删除
  104 + if ((isset($config['serverbackup']) && !$config['serverbackup']) || $force) {
  105 + if ($attachment && !empty($attachment['id'])) {
  106 + $attachment->delete();
  107 + }
  108 + if ($upload) {
  109 + //文件绝对路径
  110 + $filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
  111 + @unlink($filePath);
  112 + }
  113 + }
  114 + };
  115 +
  116 + $chunkid = $this->request->post("chunkid");
  117 + if ($chunkid) {
  118 + $action = $this->request->post("action");
  119 + $chunkindex = $this->request->post("chunkindex/d");
  120 + $chunkcount = $this->request->post("chunkcount/d");
  121 + $filesize = $this->request->post("filesize");
  122 + $filename = $this->request->post("filename");
  123 + if ($action == 'merge') {
  124 + $attachment = null;
  125 + $upload = null;
  126 + if ($config['uploadmode'] == 'server') {
  127 + //合并分片文件
  128 + try {
  129 + $upload = new Upload();
  130 + $attachment = $upload->merge($chunkid, $chunkcount, $filename);
  131 + } catch (UploadException $e) {
  132 + $this->error($e->getMessage());
  133 + }
  134 + }
  135 +
  136 + $config = get_addon_config('qiniu');
  137 +
  138 + $name = xss_clean($filename);
  139 + $config['savekey'] = (new Upload())->getSavekey($config['savekey'], $name, $md5);
  140 +
  141 + // 重新生成上传 Token
  142 + $token = $auth->uploadToken($config['bucket'], null, 3600, ['saveKey' => ltrim($config['savekey'], '/')]);
  143 +
  144 + $contexts = $this->request->post("contexts/a", []);
  145 + $uploader = new ResumeUploader($token, null, null, $filesize);
  146 + list($ret, $err) = $uploader->setContexts($contexts)->makeFile($filename);
  147 + if ($err !== null) {
  148 + $checkDeleteFile($attachment, $upload, true);
  149 + $this->error("上传失败");
  150 + } else {
  151 + $checkDeleteFile($attachment, $upload);
  152 + $this->success("上传成功", '', ['url' => '/' . $ret['key'], 'fullurl' => cdnurl('/' . $ret['key'], true), 'hash' => $ret['hash']]);
  153 + }
  154 + } else {
  155 + //默认普通上传文件
  156 + $file = $this->request->file('file');
  157 + try {
  158 + $upload = new Upload($file);
  159 + $file = $upload->chunk($chunkid, $chunkindex, $chunkcount);
  160 + } catch (UploadException $e) {
  161 + $this->error($e->getMessage());
  162 + }
  163 +
  164 + //上传分片文件
  165 + //$file = $this->request->file('file');
  166 + $filesize = $file->getSize();
  167 + //合并分片文件
  168 + $uploader = new ResumeUploader($token, null, fopen($file->getRealPath(), 'rb'), $filesize);
  169 + $ret = $uploader->uploadChunk($chunkindex, $file, $filesize);
  170 + $this->success("上传成功", "", $ret);
  171 + }
  172 + } else {
  173 + $attachment = null;
  174 + //默认普通上传文件
  175 + $file = $this->request->file('file');
  176 + try {
  177 + $upload = new Upload($file);
  178 +
  179 + $suffix = $upload->getSuffix();
  180 + $md5 = md5_file($file->getRealPath());
  181 + $filename = $file->getFilename();
  182 + $fileprefix = substr($filename, 0, strripos($filename, '.'));
  183 + $search = ['$(year)', '$(mon)', '$(day)', '$(hour)', '$(min)', '$(sec)', '$(etag)', '$(ext)', '$(fname)', '$(fprefix)'];
  184 + $replace = [date("Y"), date("m"), date("d"), date("H"), date("i"), date("s"), $md5, '.' . $suffix, $filename, $fileprefix];
  185 + $savekey = ltrim(str_replace($search, $replace, $config['savekey']), '/');
  186 +
  187 + $attachment = $upload->upload($savekey);
  188 + } catch (UploadException $e) {
  189 + $this->error($e->getMessage());
  190 + }
  191 +
  192 + //文件绝对路径
  193 + $filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
  194 +
  195 + //上传到七牛后保存的文件名
  196 + $saveKey = ltrim($attachment->url, '/');
  197 +
  198 + $url = $attachment->url;
  199 +
  200 + try {
  201 + // 调用 UploadManager 的 putFile 方法进行文件的上传。
  202 + list($ret, $err) = $uploadMgr->putFile($token, $saveKey, $filePath);
  203 +
  204 + if ($err !== null) {
  205 + throw new \Exception("上传失败");
  206 + }
  207 + //成功不做任何操作
  208 + } catch (\Exception $e) {
  209 + $checkDeleteFile($attachment, $upload, true);
  210 + $this->error("上传失败");
  211 + }
  212 + $hash = md5_file($filePath);
  213 + $checkDeleteFile($attachment, $upload);
  214 +
  215 + $this->success("上传成功", '', ['url' => $url, 'fullurl' => cdnurl($url, true), 'hash' => $hash]);
  216 + }
  217 + }
  218 +
  219 + /**
  220 + * 通知回调
  221 + */
  222 + public function notify()
  223 + {
  224 + Config::set('default_return_type', 'json');
  225 +
  226 + $this->check();
  227 + $this->request->filter('trim,strip_tags,htmlspecialchars,xss_clean');
  228 +
  229 + $size = $this->request->post('size/d');
  230 + $name = $this->request->post('name', '');
  231 + $hash = $this->request->post('hash', '');
  232 + $type = $this->request->post('type', '');
  233 + $url = $this->request->post('url', '');
  234 + $width = $this->request->post('width/d');
  235 + $height = $this->request->post('height/d');
  236 + $category = $this->request->post('category', '');
  237 + $suffix = strtolower(pathinfo($name, PATHINFO_EXTENSION));
  238 + $suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
  239 + $attachment = Attachment::where('url', $url)->where('storage', 'qiniu')->find();
  240 + if (!$attachment) {
  241 + $params = array(
  242 + 'category' => $category,
  243 + 'admin_id' => (int)session('admin.id'),
  244 + 'user_id' => (int)cookie('uid'),
  245 + 'filename' => $name,
  246 + 'filesize' => $size,
  247 + 'imagewidth' => $width,
  248 + 'imageheight' => $height,
  249 + 'imagetype' => $suffix,
  250 + 'imageframes' => 0,
  251 + 'mimetype' => $type,
  252 + 'url' => $url,
  253 + 'uploadtime' => time(),
  254 + 'storage' => 'qiniu',
  255 + 'sha1' => $hash,
  256 + );
  257 + Attachment::create($params, true);
  258 + }
  259 + $this->success();
  260 + }
  261 +
  262 + /**
  263 + * 检查签名是否正确或过期
  264 + */
  265 + protected function check()
  266 + {
  267 + $qiniutoken = $this->request->post('qiniutoken', $this->request->server('AUTHORIZATION'), 'trim');
  268 + if (!$qiniutoken) {
  269 + $this->error("参数不正确(code:1)");
  270 + }
  271 + $config = get_addon_config('qiniu');
  272 + $auth = new Auth($config['accessKey'], $config['secretKey']);
  273 + list($accessKey, $sign, $data) = explode(':', $qiniutoken);
  274 + if (!$accessKey || !$sign || !$data) {
  275 + $this->error("参数不正确(code:2)");
  276 + }
  277 + if ($accessKey !== $config['accessKey']) {
  278 + $this->error("参数不正确(code:3)");
  279 + }
  280 + if ($accessKey . ':' . $sign !== $auth->sign($data)) {
  281 + $this->error("签名不正确");
  282 + }
  283 + $json = json_decode(\Qiniu\base64_urlSafeDecode($data), true);
  284 + if ($json['deadline'] < time()) {
  285 + $this->error("请求已经超时");
  286 + }
  287 + }
  288 +}
  1 +name = qiniu
  2 +title = 七牛云储存
  3 +intro = 使用七牛云储存,支持直传、服务器中转、分片上传
  4 +author = FastAdmin
  5 +website = https://www.fastadmin.net
  6 +version = 1.2.4
  7 +state = 1
  8 +url = /addons/qiniu
  9 +license = basic
  10 +licenseto = 10789
  1 +<?php
  2 +namespace Qiniu;
  3 +
  4 +use Qiniu\Zone;
  5 +
  6 +final class Auth
  7 +{
  8 + private $accessKey;
  9 + private $secretKey;
  10 +
  11 + public function __construct($accessKey, $secretKey)
  12 + {
  13 + $this->accessKey = $accessKey;
  14 + $this->secretKey = $secretKey;
  15 + }
  16 +
  17 + public function getAccessKey()
  18 + {
  19 + return $this->accessKey;
  20 + }
  21 +
  22 + public function sign($data)
  23 + {
  24 + $hmac = hash_hmac('sha1', $data, $this->secretKey, true);
  25 + return $this->accessKey . ':' . \Qiniu\base64_urlSafeEncode($hmac);
  26 + }
  27 +
  28 + public function signWithData($data)
  29 + {
  30 + $encodedData = \Qiniu\base64_urlSafeEncode($data);
  31 + return $this->sign($encodedData) . ':' . $encodedData;
  32 + }
  33 +
  34 + public function signRequest($urlString, $body, $contentType = null)
  35 + {
  36 + $url = parse_url($urlString);
  37 + $data = '';
  38 + if (array_key_exists('path', $url)) {
  39 + $data = $url['path'];
  40 + }
  41 + if (array_key_exists('query', $url)) {
  42 + $data .= '?' . $url['query'];
  43 + }
  44 + $data .= "\n";
  45 +
  46 + if ($body !== null && $contentType === 'application/x-www-form-urlencoded') {
  47 + $data .= $body;
  48 + }
  49 + return $this->sign($data);
  50 + }
  51 +
  52 + public function verifyCallback($contentType, $originAuthorization, $url, $body)
  53 + {
  54 + $authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
  55 + return $originAuthorization === $authorization;
  56 + }
  57 +
  58 + public function privateDownloadUrl($baseUrl, $expires = 3600)
  59 + {
  60 + $deadline = time() + $expires;
  61 +
  62 + $pos = strpos($baseUrl, '?');
  63 + if ($pos !== false) {
  64 + $baseUrl .= '&e=';
  65 + } else {
  66 + $baseUrl .= '?e=';
  67 + }
  68 + $baseUrl .= $deadline;
  69 +
  70 + $token = $this->sign($baseUrl);
  71 + return "$baseUrl&token=$token";
  72 + }
  73 +
  74 + public function uploadToken($bucket, $key = null, $expires = 3600, $policy = null, $strictPolicy = true)
  75 + {
  76 + $deadline = time() + $expires;
  77 + $scope = $bucket;
  78 + if ($key !== null) {
  79 + $scope .= ':' . $key;
  80 + }
  81 +
  82 + $args = self::copyPolicy($args, $policy, $strictPolicy);
  83 + $args['scope'] = $scope;
  84 + $args['deadline'] = $deadline;
  85 +
  86 + $b = json_encode($args);
  87 + return $this->signWithData($b);
  88 + }
  89 +
  90 + /**
  91 + *上传策略,参数规格详见
  92 + *http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
  93 + */
  94 + private static $policyFields = array(
  95 + 'callbackUrl',
  96 + 'callbackBody',
  97 + 'callbackHost',
  98 + 'callbackBodyType',
  99 + 'callbackFetchKey',
  100 +
  101 + 'returnUrl',
  102 + 'returnBody',
  103 +
  104 + 'endUser',
  105 + 'saveKey',
  106 + 'insertOnly',
  107 +
  108 + 'detectMime',
  109 + 'mimeLimit',
  110 + 'fsizeMin',
  111 + 'fsizeLimit',
  112 +
  113 + 'persistentOps',
  114 + 'persistentNotifyUrl',
  115 + 'persistentPipeline',
  116 +
  117 + 'deleteAfterDays',
  118 + 'fileType',
  119 + 'isPrefixalScope',
  120 + );
  121 +
  122 + private static function copyPolicy(&$policy, $originPolicy, $strictPolicy)
  123 + {
  124 + if ($originPolicy === null) {
  125 + return array();
  126 + }
  127 + foreach ($originPolicy as $key => $value) {
  128 + if (!$strictPolicy || in_array((string)$key, self::$policyFields, true)) {
  129 + $policy[$key] = $value;
  130 + }
  131 + }
  132 + return $policy;
  133 + }
  134 +
  135 + public function authorization($url, $body = null, $contentType = null)
  136 + {
  137 + $authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
  138 + return array('Authorization' => $authorization);
  139 + }
  140 +
  141 + public function authorizationV2($url, $method, $body = null, $contentType = null)
  142 + {
  143 + $urlItems = parse_url($url);
  144 + $host = $urlItems['host'];
  145 +
  146 + if (isset($urlItems['port'])) {
  147 + $port = $urlItems['port'];
  148 + } else {
  149 + $port = '';
  150 + }
  151 +
  152 + $path = $urlItems['path'];
  153 + if (isset($urlItems['query'])) {
  154 + $query = $urlItems['query'];
  155 + } else {
  156 + $query = '';
  157 + }
  158 +
  159 + //write request uri
  160 + $toSignStr = $method . ' ' . $path;
  161 + if (!empty($query)) {
  162 + $toSignStr .= '?' . $query;
  163 + }
  164 +
  165 + //write host and port
  166 + $toSignStr .= "\nHost: " . $host;
  167 + if (!empty($port)) {
  168 + $toSignStr .= ":" . $port;
  169 + }
  170 +
  171 + //write content type
  172 + if (!empty($contentType)) {
  173 + $toSignStr .= "\nContent-Type: " . $contentType;
  174 + }
  175 +
  176 + $toSignStr .= "\n\n";
  177 +
  178 + //write body
  179 + if (!empty($body)) {
  180 + $toSignStr .= $body;
  181 + }
  182 +
  183 + $sign = $this->sign($toSignStr);
  184 + $auth = 'Qiniu ' . $sign;
  185 + return array('Authorization' => $auth);
  186 + }
  187 +}
  1 +<?php
  2 +
  3 +namespace Qiniu\Cdn;
  4 +
  5 +use Qiniu\Auth;
  6 +use Qiniu\Http\Error;
  7 +use Qiniu\Http\Client;
  8 +
  9 +final class CdnManager
  10 +{
  11 +
  12 + private $auth;
  13 + private $server;
  14 +
  15 + public function __construct(Auth $auth)
  16 + {
  17 + $this->auth = $auth;
  18 + $this->server = 'http://fusion.qiniuapi.com';
  19 + }
  20 +
  21 + /**
  22 + * @param array $urls 待刷新的文件链接数组
  23 + * @return array
  24 + */
  25 + public function refreshUrls(array $urls)
  26 + {
  27 + return $this->refreshUrlsAndDirs($urls, array());
  28 + }
  29 +
  30 + /**
  31 + * @param array $dirs 待刷新的文件链接数组
  32 + * @return array
  33 + * 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
  34 + * 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
  35 + */
  36 + public function refreshDirs(array $dirs)
  37 + {
  38 + return $this->refreshUrlsAndDirs(array(), $dirs);
  39 + }
  40 +
  41 + /**
  42 + * @param array $urls 待刷新的文件链接数组
  43 + * @param array $dirs 待刷新的目录链接数组
  44 + *
  45 + * @return array 刷新的请求回复和错误,参考 examples/cdn_manager.php 代码
  46 + * @link http://developer.qiniu.com/article/fusion/api/refresh.html
  47 + *
  48 + * 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
  49 + * 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
  50 + */
  51 + public function refreshUrlsAndDirs(array $urls, array $dirs)
  52 + {
  53 + $req = array();
  54 + if (!empty($urls)) {
  55 + $req['urls'] = $urls;
  56 + }
  57 + if (!empty($dirs)) {
  58 + $req['dirs'] = $dirs;
  59 + }
  60 +
  61 + $url = $this->server . '/v2/tune/refresh';
  62 + $body = json_encode($req);
  63 + return $this->post($url, $body);
  64 + }
  65 +
  66 + /**
  67 + * @param array $urls 待预取的文件链接数组
  68 + *
  69 + * @return array 预取的请求回复和错误,参考 examples/cdn_manager.php 代码
  70 + *
  71 + * @link http://developer.qiniu.com/article/fusion/api/refresh.html
  72 + */
  73 + public function prefetchUrls(array $urls)
  74 + {
  75 + $req = array(
  76 + 'urls' => $urls,
  77 + );
  78 +
  79 + $url = $this->server . '/v2/tune/prefetch';
  80 + $body = json_encode($req);
  81 + return $this->post($url, $body);
  82 + }
  83 +
  84 + /**
  85 + * @param array $domains 待获取带宽数据的域名数组
  86 + * @param string $startDate 开始的日期,格式类似 2017-01-01
  87 + * @param string $endDate 结束的日期,格式类似 2017-01-01
  88 + * @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
  89 + *
  90 + * @return array 带宽数据和错误信息,参考 examples/cdn_manager.php 代码
  91 + *
  92 + * @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
  93 + */
  94 + public function getBandwidthData(array $domains, $startDate, $endDate, $granularity)
  95 + {
  96 + $req = array();
  97 + $req['domains'] = implode(';', $domains);
  98 + $req['startDate'] = $startDate;
  99 + $req['endDate'] = $endDate;
  100 + $req['granularity'] = $granularity;
  101 +
  102 + $url = $this->server . '/v2/tune/bandwidth';
  103 + $body = json_encode($req);
  104 + return $this->post($url, $body);
  105 + }
  106 +
  107 + /**
  108 + * @param array $domains 待获取流量数据的域名数组
  109 + * @param string $startDate 开始的日期,格式类似 2017-01-01
  110 + * @param string $endDate 结束的日期,格式类似 2017-01-01
  111 + * @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
  112 + *
  113 + * @return array 流量数据和错误信息,参考 examples/cdn_manager.php 代码
  114 + *
  115 + * @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
  116 + */
  117 + public function getFluxData(array $domains, $startDate, $endDate, $granularity)
  118 + {
  119 + $req = array();
  120 + $req['domains'] = implode(';', $domains);
  121 + $req['startDate'] = $startDate;
  122 + $req['endDate'] = $endDate;
  123 + $req['granularity'] = $granularity;
  124 +
  125 + $url = $this->server . '/v2/tune/flux';
  126 + $body = json_encode($req);
  127 + return $this->post($url, $body);
  128 + }
  129 +
  130 + /**
  131 + * @param array $domains 待获取日志下载链接的域名数组
  132 + * @param string $logDate 获取指定日期的日志下载链接,格式类似 2017-01-01
  133 + *
  134 + * @return array 日志下载链接数据和错误信息,参考 examples/cdn_manager.php 代码
  135 + *
  136 + * @link http://developer.qiniu.com/article/fusion/api/log.html
  137 + */
  138 + public function getCdnLogList(array $domains, $logDate)
  139 + {
  140 + $req = array();
  141 + $req['domains'] = implode(';', $domains);
  142 + $req['day'] = $logDate;
  143 +
  144 + $url = $this->server . '/v2/tune/log/list';
  145 + $body = json_encode($req);
  146 + return $this->post($url, $body);
  147 + }
  148 +
  149 + private function post($url, $body)
  150 + {
  151 + $headers = $this->auth->authorization($url, $body, 'application/json');
  152 + $headers['Content-Type'] = 'application/json';
  153 + $ret = Client::post($url, $body, $headers);
  154 + if (!$ret->ok()) {
  155 + return array(null, new Error($url, $ret));
  156 + }
  157 + $r = ($ret->body === null) ? array() : $ret->json();
  158 + return array($r, null);
  159 + }
  160 +
  161 + /**
  162 + * 构建时间戳防盗链鉴权的访问外链
  163 + *
  164 + * @param string $rawUrl 需要签名的资源url
  165 + * @param string $encryptKey 时间戳防盗链密钥
  166 + * @param string $durationInSeconds 链接的有效期(以秒为单位)
  167 + *
  168 + * @return string 带鉴权信息的资源外链,参考 examples/cdn_timestamp_antileech.php 代码
  169 + */
  170 + public static function createTimestampAntiLeechUrl($rawUrl, $encryptKey, $durationInSeconds)
  171 + {
  172 + $parsedUrl = parse_url($rawUrl);
  173 + $deadline = time() + $durationInSeconds;
  174 + $expireHex = dechex($deadline);
  175 + $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
  176 + $strToSign = $encryptKey . $path . $expireHex;
  177 + $signStr = md5($strToSign);
  178 + if (isset($parsedUrl['query'])) {
  179 + $signedUrl = $rawUrl . '&sign=' . $signStr . '&t=' . $expireHex;
  180 + } else {
  181 + $signedUrl = $rawUrl . '?sign=' . $signStr . '&t=' . $expireHex;
  182 + }
  183 + return $signedUrl;
  184 + }
  185 +}
  1 +<?php
  2 +namespace Qiniu;
  3 +
  4 +final class Config
  5 +{
  6 + const SDK_VER = '7.2.10';
  7 +
  8 + const BLOCK_SIZE = 4194304; //4*1024*1024 分块上传块大小,该参数为接口规格,不能修改
  9 +
  10 + const RSF_HOST = 'rsf.qiniu.com';
  11 + const API_HOST = 'api.qiniu.com';
  12 + const RS_HOST = 'rs.qiniu.com'; //RS Host
  13 + const UC_HOST = 'uc.qbox.me'; //UC Host
  14 + const RTCAPI_HOST = 'http://rtc.qiniuapi.com';
  15 + const ARGUS_HOST = 'argus.atlab.ai';
  16 + const CASTER_HOST = 'pili-caster.qiniuapi.com';
  17 + const SMS_HOST="https://sms.qiniuapi.com";
  18 + const RTCAPI_VERSION = 'v3';
  19 + const SMS_VERSION='v1';
  20 +
  21 + // Zone 空间对应的存储区域
  22 + public $region;
  23 + //BOOL 是否使用https域名
  24 + public $useHTTPS;
  25 + //BOOL 是否使用CDN加速上传域名
  26 + public $useCdnDomains;
  27 + // Zone Cache
  28 + private $regionCache;
  29 +
  30 + // 构造函数
  31 + public function __construct(Region $z = null)
  32 + {
  33 + $this->zone = $z;
  34 + $this->useHTTPS = false;
  35 + $this->useCdnDomains = false;
  36 + $this->regionCache = array();
  37 + }
  38 +
  39 + public function getUpHost($accessKey, $bucket)
  40 + {
  41 + $region = $this->getRegion($accessKey, $bucket);
  42 + if ($this->useHTTPS === true) {
  43 + $scheme = "https://";
  44 + } else {
  45 + $scheme = "http://";
  46 + }
  47 +
  48 + $host = $region->srcUpHosts[0];
  49 + if ($this->useCdnDomains === true) {
  50 + $host = $region->cdnUpHosts[0];
  51 + }
  52 +
  53 + return $scheme . $host;
  54 + }
  55 +
  56 + public function getUpBackupHost($accessKey, $bucket)
  57 + {
  58 + $region = $this->getRegion($accessKey, $bucket);
  59 + if ($this->useHTTPS === true) {
  60 + $scheme = "https://";
  61 + } else {
  62 + $scheme = "http://";
  63 + }
  64 +
  65 + $host = $region->cdnUpHosts[0];
  66 + if ($this->useCdnDomains === true) {
  67 + $host = $region->srcUpHosts[0];
  68 + }
  69 +
  70 + return $scheme . $host;
  71 + }
  72 +
  73 + public function getRsHost($accessKey, $bucket)
  74 + {
  75 + $region = $this->getRegion($accessKey, $bucket);
  76 +
  77 + if ($this->useHTTPS === true) {
  78 + $scheme = "https://";
  79 + } else {
  80 + $scheme = "http://";
  81 + }
  82 +
  83 + return $scheme . $region->rsHost;
  84 + }
  85 +
  86 + public function getRsfHost($accessKey, $bucket)
  87 + {
  88 + $region = $this->getRegion($accessKey, $bucket);
  89 +
  90 + if ($this->useHTTPS === true) {
  91 + $scheme = "https://";
  92 + } else {
  93 + $scheme = "http://";
  94 + }
  95 +
  96 + return $scheme . $region->rsfHost;
  97 + }
  98 +
  99 + public function getIovipHost($accessKey, $bucket)
  100 + {
  101 + $region = $this->getRegion($accessKey, $bucket);
  102 +
  103 + if ($this->useHTTPS === true) {
  104 + $scheme = "https://";
  105 + } else {
  106 + $scheme = "http://";
  107 + }
  108 +
  109 + return $scheme . $region->iovipHost;
  110 + }
  111 +
  112 + public function getApiHost($accessKey, $bucket)
  113 + {
  114 + $region = $this->getRegion($accessKey, $bucket);
  115 +
  116 + if ($this->useHTTPS === true) {
  117 + $scheme = "https://";
  118 + } else {
  119 + $scheme = "http://";
  120 + }
  121 +
  122 + return $scheme . $region->apiHost;
  123 + }
  124 +
  125 + private function getRegion($accessKey, $bucket)
  126 + {
  127 + $cacheId = "$accessKey:$bucket";
  128 +
  129 + if (isset($this->regionCache[$cacheId])) {
  130 + $region = $this->regionCache[$cacheId];
  131 + } elseif (isset($this->zone)) {
  132 + $region = $this->zone;
  133 + $this->regionCache[$cacheId] = $region;
  134 + } else {
  135 + $region = Zone::queryZone($accessKey, $bucket);
  136 + $this->regionCache[$cacheId] = $region;
  137 + }
  138 + return $region;
  139 + }
  140 +}
  1 +<?php
  2 +
  3 +namespace Qiniu;
  4 +
  5 +use Qiniu\Config;
  6 +
  7 +final class Etag
  8 +{
  9 + private static function packArray($v, $a)
  10 + {
  11 + return call_user_func_array('pack', array_merge(array($v), (array)$a));
  12 + }
  13 +
  14 + private static function blockCount($fsize)
  15 + {
  16 + return intval(($fsize + (Config::BLOCK_SIZE - 1)) / Config::BLOCK_SIZE);
  17 + }
  18 +
  19 + private static function calcSha1($data)
  20 + {
  21 + $sha1Str = sha1($data, true);
  22 + $err = error_get_last();
  23 + if ($err !== null) {
  24 + return array(null, $err);
  25 + }
  26 + $byteArray = unpack('C*', $sha1Str);
  27 + return array($byteArray, null);
  28 + }
  29 +
  30 +
  31 + public static function sum($filename)
  32 + {
  33 + $fhandler = fopen($filename, 'r');
  34 + $err = error_get_last();
  35 + if ($err !== null) {
  36 + return array(null, $err);
  37 + }
  38 +
  39 + $fstat = fstat($fhandler);
  40 + $fsize = $fstat['size'];
  41 + if ((int)$fsize === 0) {
  42 + fclose($fhandler);
  43 + return array('Fto5o-5ea0sNMlW_75VgGJCv2AcJ', null);
  44 + }
  45 + $blockCnt = self::blockCount($fsize);
  46 + $sha1Buf = array();
  47 +
  48 + if ($blockCnt <= 1) {
  49 + array_push($sha1Buf, 0x16);
  50 + $fdata = fread($fhandler, Config::BLOCK_SIZE);
  51 + if ($err !== null) {
  52 + fclose($fhandler);
  53 + return array(null, $err);
  54 + }
  55 + list($sha1Code,) = self::calcSha1($fdata);
  56 + $sha1Buf = array_merge($sha1Buf, $sha1Code);
  57 + } else {
  58 + array_push($sha1Buf, 0x96);
  59 + $sha1BlockBuf = array();
  60 + for ($i = 0; $i < $blockCnt; $i++) {
  61 + $fdata = fread($fhandler, Config::BLOCK_SIZE);
  62 + list($sha1Code, $err) = self::calcSha1($fdata);
  63 + if ($err !== null) {
  64 + fclose($fhandler);
  65 + return array(null, $err);
  66 + }
  67 + $sha1BlockBuf = array_merge($sha1BlockBuf, $sha1Code);
  68 + }
  69 + $tmpData = self::packArray('C*', $sha1BlockBuf);
  70 + list($sha1Final,) = self::calcSha1($tmpData);
  71 + $sha1Buf = array_merge($sha1Buf, $sha1Final);
  72 + }
  73 + $etag = \Qiniu\base64_urlSafeEncode(self::packArray('C*', $sha1Buf));
  74 + return array($etag, null);
  75 + }
  76 +}