BarcodeGenerator.php 91 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852
  1. <?php
  2. /**
  3. * General PHP Barcode Generator
  4. *
  5. * @author Casper Bakker - picqer.com
  6. * Based on TCPDF Barcode Generator
  7. */
  8. // Copyright (C) 2002-2015 Nicola Asuni - Tecnick.com LTD
  9. //
  10. // This file is part of TCPDF software library.
  11. //
  12. // TCPDF is free software: you can redistribute it and/or modify it
  13. // under the terms of the GNU Lesser General Public License as
  14. // published by the Free Software Foundation, either version 3 of the
  15. // License, or (at your option) any later version.
  16. //
  17. // TCPDF is distributed in the hope that it will be useful, but
  18. // WITHOUT ANY WARRANTY; without even the implied warranty of
  19. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  20. // See the GNU Lesser General Public License for more details.
  21. //
  22. // You should have received a copy of the License
  23. // along with TCPDF. If not, see
  24. // <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
  25. //
  26. // See LICENSE.TXT file for more information.
  27. namespace Picqer\Barcode;
  28. use Picqer\Barcode\Exceptions\BarcodeException;
  29. use Picqer\Barcode\Exceptions\InvalidCharacterException;
  30. use Picqer\Barcode\Exceptions\InvalidCheckDigitException;
  31. use Picqer\Barcode\Exceptions\InvalidFormatException;
  32. use Picqer\Barcode\Exceptions\InvalidLengthException;
  33. use Picqer\Barcode\Exceptions\UnknownTypeException;
  34. abstract class BarcodeGenerator
  35. {
  36. const TYPE_CODE_39 = 'C39';
  37. const TYPE_CODE_39_CHECKSUM = 'C39+';
  38. const TYPE_CODE_39E = 'C39E';
  39. const TYPE_CODE_39E_CHECKSUM = 'C39E+';
  40. const TYPE_CODE_93 = 'C93';
  41. const TYPE_STANDARD_2_5 = 'S25';
  42. const TYPE_STANDARD_2_5_CHECKSUM = 'S25+';
  43. const TYPE_INTERLEAVED_2_5 = 'I25';
  44. const TYPE_INTERLEAVED_2_5_CHECKSUM = 'I25+';
  45. const TYPE_CODE_128 = 'C128';
  46. const TYPE_CODE_128_A = 'C128A';
  47. const TYPE_CODE_128_B = 'C128B';
  48. const TYPE_CODE_128_C = 'C128C';
  49. const TYPE_EAN_2 = 'EAN2';
  50. const TYPE_EAN_5 = 'EAN5';
  51. const TYPE_EAN_8 = 'EAN8';
  52. const TYPE_EAN_13 = 'EAN13';
  53. const TYPE_UPC_A = 'UPCA';
  54. const TYPE_UPC_E = 'UPCE';
  55. const TYPE_MSI = 'MSI';
  56. const TYPE_MSI_CHECKSUM = 'MSI+';
  57. const TYPE_POSTNET = 'POSTNET';
  58. const TYPE_PLANET = 'PLANET';
  59. const TYPE_RMS4CC = 'RMS4CC';
  60. const TYPE_KIX = 'KIX';
  61. const TYPE_IMB = 'IMB';
  62. const TYPE_CODABAR = 'CODABAR';
  63. const TYPE_CODE_11 = 'CODE11';
  64. const TYPE_PHARMA_CODE = 'PHARMA';
  65. const TYPE_PHARMA_CODE_TWO_TRACKS = 'PHARMA2T';
  66. /**
  67. * Get the barcode data
  68. *
  69. * @param string $code code to print
  70. * @param string $type type of barcode
  71. * @return array barcode array
  72. * @public
  73. */
  74. protected function getBarcodeData($code, $type)
  75. {
  76. switch (strtoupper($type)) {
  77. case self::TYPE_CODE_39: { // CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
  78. $arrcode = $this->barcode_code39($code, false, false);
  79. break;
  80. }
  81. case self::TYPE_CODE_39_CHECKSUM: { // CODE 39 with checksum
  82. $arrcode = $this->barcode_code39($code, false, true);
  83. break;
  84. }
  85. case self::TYPE_CODE_39E: { // CODE 39 EXTENDED
  86. $arrcode = $this->barcode_code39($code, true, false);
  87. break;
  88. }
  89. case self::TYPE_CODE_39E_CHECKSUM: { // CODE 39 EXTENDED + CHECKSUM
  90. $arrcode = $this->barcode_code39($code, true, true);
  91. break;
  92. }
  93. case self::TYPE_CODE_93: { // CODE 93 - USS-93
  94. $arrcode = $this->barcode_code93($code);
  95. break;
  96. }
  97. case self::TYPE_STANDARD_2_5: { // Standard 2 of 5
  98. $arrcode = $this->barcode_s25($code, false);
  99. break;
  100. }
  101. case self::TYPE_STANDARD_2_5_CHECKSUM: { // Standard 2 of 5 + CHECKSUM
  102. $arrcode = $this->barcode_s25($code, true);
  103. break;
  104. }
  105. case self::TYPE_INTERLEAVED_2_5: { // Interleaved 2 of 5
  106. $arrcode = $this->barcode_i25($code, false);
  107. break;
  108. }
  109. case self::TYPE_INTERLEAVED_2_5_CHECKSUM: { // Interleaved 2 of 5 + CHECKSUM
  110. $arrcode = $this->barcode_i25($code, true);
  111. break;
  112. }
  113. case self::TYPE_CODE_128: { // CODE 128
  114. $arrcode = $this->barcode_c128($code, '');
  115. break;
  116. }
  117. case self::TYPE_CODE_128_A: { // CODE 128 A
  118. $arrcode = $this->barcode_c128($code, 'A');
  119. break;
  120. }
  121. case self::TYPE_CODE_128_B: { // CODE 128 B
  122. $arrcode = $this->barcode_c128($code, 'B');
  123. break;
  124. }
  125. case self::TYPE_CODE_128_C: { // CODE 128 C
  126. $arrcode = $this->barcode_c128($code, 'C');
  127. break;
  128. }
  129. case self::TYPE_EAN_2: { // 2-Digits UPC-Based Extention
  130. $arrcode = $this->barcode_eanext($code, 2);
  131. break;
  132. }
  133. case self::TYPE_EAN_5: { // 5-Digits UPC-Based Extention
  134. $arrcode = $this->barcode_eanext($code, 5);
  135. break;
  136. }
  137. case self::TYPE_EAN_8: { // EAN 8
  138. $arrcode = $this->barcode_eanupc($code, 8);
  139. break;
  140. }
  141. case self::TYPE_EAN_13: { // EAN 13
  142. $arrcode = $this->barcode_eanupc($code, 13);
  143. break;
  144. }
  145. case self::TYPE_UPC_A: { // UPC-A
  146. $arrcode = $this->barcode_eanupc($code, 12);
  147. break;
  148. }
  149. case self::TYPE_UPC_E: { // UPC-E
  150. $arrcode = $this->barcode_eanupc($code, 6);
  151. break;
  152. }
  153. case self::TYPE_MSI: { // MSI (Variation of Plessey code)
  154. $arrcode = $this->barcode_msi($code, false);
  155. break;
  156. }
  157. case self::TYPE_MSI_CHECKSUM: { // MSI + CHECKSUM (modulo 11)
  158. $arrcode = $this->barcode_msi($code, true);
  159. break;
  160. }
  161. case self::TYPE_POSTNET: { // POSTNET
  162. $arrcode = $this->barcode_postnet($code, false);
  163. break;
  164. }
  165. case self::TYPE_PLANET: { // PLANET
  166. $arrcode = $this->barcode_postnet($code, true);
  167. break;
  168. }
  169. case self::TYPE_RMS4CC: { // RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
  170. $arrcode = $this->barcode_rms4cc($code, false);
  171. break;
  172. }
  173. case self::TYPE_KIX: { // KIX (Klant index - Customer index)
  174. $arrcode = $this->barcode_rms4cc($code, true);
  175. break;
  176. }
  177. case self::TYPE_IMB: { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
  178. $arrcode = $this->barcode_imb($code);
  179. break;
  180. }
  181. case self::TYPE_CODABAR: { // CODABAR
  182. $arrcode = $this->barcode_codabar($code);
  183. break;
  184. }
  185. case self::TYPE_CODE_11: { // CODE 11
  186. $arrcode = $this->barcode_code11($code);
  187. break;
  188. }
  189. case self::TYPE_PHARMA_CODE: { // PHARMACODE
  190. $arrcode = $this->barcode_pharmacode($code);
  191. break;
  192. }
  193. case self::TYPE_PHARMA_CODE_TWO_TRACKS: { // PHARMACODE TWO-TRACKS
  194. $arrcode = $this->barcode_pharmacode2t($code);
  195. break;
  196. }
  197. default: {
  198. throw new UnknownTypeException();
  199. break;
  200. }
  201. }
  202. if ( ! isset($arrcode['maxWidth'])) {
  203. $arrcode = $this->convertBarcodeArrayToNewStyle($arrcode);
  204. }
  205. return $arrcode;
  206. }
  207. /**
  208. * CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
  209. * General-purpose code in very wide use world-wide
  210. *
  211. * @param $code (string) code to represent.
  212. * @param $extended (boolean) if true uses the extended mode.
  213. * @param $checksum (boolean) if true add a checksum to the code.
  214. * @return array barcode representation.
  215. * @protected
  216. */
  217. protected function barcode_code39($code, $extended = false, $checksum = false)
  218. {
  219. $chr = [];
  220. $chr['0'] = '111331311';
  221. $chr['1'] = '311311113';
  222. $chr['2'] = '113311113';
  223. $chr['3'] = '313311111';
  224. $chr['4'] = '111331113';
  225. $chr['5'] = '311331111';
  226. $chr['6'] = '113331111';
  227. $chr['7'] = '111311313';
  228. $chr['8'] = '311311311';
  229. $chr['9'] = '113311311';
  230. $chr['A'] = '311113113';
  231. $chr['B'] = '113113113';
  232. $chr['C'] = '313113111';
  233. $chr['D'] = '111133113';
  234. $chr['E'] = '311133111';
  235. $chr['F'] = '113133111';
  236. $chr['G'] = '111113313';
  237. $chr['H'] = '311113311';
  238. $chr['I'] = '113113311';
  239. $chr['J'] = '111133311';
  240. $chr['K'] = '311111133';
  241. $chr['L'] = '113111133';
  242. $chr['M'] = '313111131';
  243. $chr['N'] = '111131133';
  244. $chr['O'] = '311131131';
  245. $chr['P'] = '113131131';
  246. $chr['Q'] = '111111333';
  247. $chr['R'] = '311111331';
  248. $chr['S'] = '113111331';
  249. $chr['T'] = '111131331';
  250. $chr['U'] = '331111113';
  251. $chr['V'] = '133111113';
  252. $chr['W'] = '333111111';
  253. $chr['X'] = '131131113';
  254. $chr['Y'] = '331131111';
  255. $chr['Z'] = '133131111';
  256. $chr['-'] = '131111313';
  257. $chr['.'] = '331111311';
  258. $chr[' '] = '133111311';
  259. $chr['$'] = '131313111';
  260. $chr['/'] = '131311131';
  261. $chr['+'] = '131113131';
  262. $chr['%'] = '111313131';
  263. $chr['*'] = '131131311';
  264. $code = strtoupper($code);
  265. if ($extended) {
  266. // extended mode
  267. $code = $this->encode_code39_ext($code);
  268. }
  269. if ($checksum) {
  270. // checksum
  271. $code .= $this->checksum_code39($code);
  272. }
  273. // add start and stop codes
  274. $code = '*' . $code . '*';
  275. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  276. $k = 0;
  277. $clen = strlen($code);
  278. for ($i = 0; $i < $clen; ++$i) {
  279. $char = $code{$i};
  280. if ( ! isset($chr[$char])) {
  281. throw new InvalidCharacterException('Char ' . $char . ' is unsupported');
  282. }
  283. for ($j = 0; $j < 9; ++$j) {
  284. if (($j % 2) == 0) {
  285. $t = true; // bar
  286. } else {
  287. $t = false; // space
  288. }
  289. $w = $chr[$char]{$j};
  290. $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  291. $bararray['maxw'] += $w;
  292. ++$k;
  293. }
  294. // intercharacter gap
  295. $bararray['bcode'][$k] = array('t' => false, 'w' => 1, 'h' => 1, 'p' => 0);
  296. $bararray['maxw'] += 1;
  297. ++$k;
  298. }
  299. return $bararray;
  300. }
  301. /**
  302. * Encode a string to be used for CODE 39 Extended mode.
  303. *
  304. * @param string $code code to represent.
  305. * @return bool|string encoded string.
  306. * @protected
  307. */
  308. protected function encode_code39_ext($code)
  309. {
  310. $encode = array(
  311. chr(0) => '%U',
  312. chr(1) => '$A',
  313. chr(2) => '$B',
  314. chr(3) => '$C',
  315. chr(4) => '$D',
  316. chr(5) => '$E',
  317. chr(6) => '$F',
  318. chr(7) => '$G',
  319. chr(8) => '$H',
  320. chr(9) => '$I',
  321. chr(10) => '$J',
  322. chr(11) => '£K',
  323. chr(12) => '$L',
  324. chr(13) => '$M',
  325. chr(14) => '$N',
  326. chr(15) => '$O',
  327. chr(16) => '$P',
  328. chr(17) => '$Q',
  329. chr(18) => '$R',
  330. chr(19) => '$S',
  331. chr(20) => '$T',
  332. chr(21) => '$U',
  333. chr(22) => '$V',
  334. chr(23) => '$W',
  335. chr(24) => '$X',
  336. chr(25) => '$Y',
  337. chr(26) => '$Z',
  338. chr(27) => '%A',
  339. chr(28) => '%B',
  340. chr(29) => '%C',
  341. chr(30) => '%D',
  342. chr(31) => '%E',
  343. chr(32) => ' ',
  344. chr(33) => '/A',
  345. chr(34) => '/B',
  346. chr(35) => '/C',
  347. chr(36) => '/D',
  348. chr(37) => '/E',
  349. chr(38) => '/F',
  350. chr(39) => '/G',
  351. chr(40) => '/H',
  352. chr(41) => '/I',
  353. chr(42) => '/J',
  354. chr(43) => '/K',
  355. chr(44) => '/L',
  356. chr(45) => '-',
  357. chr(46) => '.',
  358. chr(47) => '/O',
  359. chr(48) => '0',
  360. chr(49) => '1',
  361. chr(50) => '2',
  362. chr(51) => '3',
  363. chr(52) => '4',
  364. chr(53) => '5',
  365. chr(54) => '6',
  366. chr(55) => '7',
  367. chr(56) => '8',
  368. chr(57) => '9',
  369. chr(58) => '/Z',
  370. chr(59) => '%F',
  371. chr(60) => '%G',
  372. chr(61) => '%H',
  373. chr(62) => '%I',
  374. chr(63) => '%J',
  375. chr(64) => '%V',
  376. chr(65) => 'A',
  377. chr(66) => 'B',
  378. chr(67) => 'C',
  379. chr(68) => 'D',
  380. chr(69) => 'E',
  381. chr(70) => 'F',
  382. chr(71) => 'G',
  383. chr(72) => 'H',
  384. chr(73) => 'I',
  385. chr(74) => 'J',
  386. chr(75) => 'K',
  387. chr(76) => 'L',
  388. chr(77) => 'M',
  389. chr(78) => 'N',
  390. chr(79) => 'O',
  391. chr(80) => 'P',
  392. chr(81) => 'Q',
  393. chr(82) => 'R',
  394. chr(83) => 'S',
  395. chr(84) => 'T',
  396. chr(85) => 'U',
  397. chr(86) => 'V',
  398. chr(87) => 'W',
  399. chr(88) => 'X',
  400. chr(89) => 'Y',
  401. chr(90) => 'Z',
  402. chr(91) => '%K',
  403. chr(92) => '%L',
  404. chr(93) => '%M',
  405. chr(94) => '%N',
  406. chr(95) => '%O',
  407. chr(96) => '%W',
  408. chr(97) => '+A',
  409. chr(98) => '+B',
  410. chr(99) => '+C',
  411. chr(100) => '+D',
  412. chr(101) => '+E',
  413. chr(102) => '+F',
  414. chr(103) => '+G',
  415. chr(104) => '+H',
  416. chr(105) => '+I',
  417. chr(106) => '+J',
  418. chr(107) => '+K',
  419. chr(108) => '+L',
  420. chr(109) => '+M',
  421. chr(110) => '+N',
  422. chr(111) => '+O',
  423. chr(112) => '+P',
  424. chr(113) => '+Q',
  425. chr(114) => '+R',
  426. chr(115) => '+S',
  427. chr(116) => '+T',
  428. chr(117) => '+U',
  429. chr(118) => '+V',
  430. chr(119) => '+W',
  431. chr(120) => '+X',
  432. chr(121) => '+Y',
  433. chr(122) => '+Z',
  434. chr(123) => '%P',
  435. chr(124) => '%Q',
  436. chr(125) => '%R',
  437. chr(126) => '%S',
  438. chr(127) => '%T'
  439. );
  440. $code_ext = '';
  441. $clen = strlen($code);
  442. for ($i = 0; $i < $clen; ++$i) {
  443. if (ord($code{$i}) > 127) {
  444. throw new InvalidCharacterException('Only supports till char 127');
  445. }
  446. $code_ext .= $encode[$code{$i}];
  447. }
  448. return $code_ext;
  449. }
  450. /**
  451. * Calculate CODE 39 checksum (modulo 43).
  452. *
  453. * @param string $code code to represent.
  454. * @return string char checksum.
  455. * @protected
  456. */
  457. protected function checksum_code39($code)
  458. {
  459. $chars = array(
  460. '0',
  461. '1',
  462. '2',
  463. '3',
  464. '4',
  465. '5',
  466. '6',
  467. '7',
  468. '8',
  469. '9',
  470. 'A',
  471. 'B',
  472. 'C',
  473. 'D',
  474. 'E',
  475. 'F',
  476. 'G',
  477. 'H',
  478. 'I',
  479. 'J',
  480. 'K',
  481. 'L',
  482. 'M',
  483. 'N',
  484. 'O',
  485. 'P',
  486. 'Q',
  487. 'R',
  488. 'S',
  489. 'T',
  490. 'U',
  491. 'V',
  492. 'W',
  493. 'X',
  494. 'Y',
  495. 'Z',
  496. '-',
  497. '.',
  498. ' ',
  499. '$',
  500. '/',
  501. '+',
  502. '%'
  503. );
  504. $sum = 0;
  505. $codelength = strlen($code);
  506. for ($i = 0; $i < $codelength; ++$i) {
  507. $k = array_keys($chars, $code{$i});
  508. $sum += $k[0];
  509. }
  510. $j = ($sum % 43);
  511. return $chars[$j];
  512. }
  513. /**
  514. * CODE 93 - USS-93
  515. * Compact code similar to Code 39
  516. *
  517. * @param $code (string) code to represent.
  518. * @return array barcode representation.
  519. * @protected
  520. */
  521. protected function barcode_code93($code)
  522. {
  523. $chr = [];
  524. $chr[48] = '131112'; // 0
  525. $chr[49] = '111213'; // 1
  526. $chr[50] = '111312'; // 2
  527. $chr[51] = '111411'; // 3
  528. $chr[52] = '121113'; // 4
  529. $chr[53] = '121212'; // 5
  530. $chr[54] = '121311'; // 6
  531. $chr[55] = '111114'; // 7
  532. $chr[56] = '131211'; // 8
  533. $chr[57] = '141111'; // 9
  534. $chr[65] = '211113'; // A
  535. $chr[66] = '211212'; // B
  536. $chr[67] = '211311'; // C
  537. $chr[68] = '221112'; // D
  538. $chr[69] = '221211'; // E
  539. $chr[70] = '231111'; // F
  540. $chr[71] = '112113'; // G
  541. $chr[72] = '112212'; // H
  542. $chr[73] = '112311'; // I
  543. $chr[74] = '122112'; // J
  544. $chr[75] = '132111'; // K
  545. $chr[76] = '111123'; // L
  546. $chr[77] = '111222'; // M
  547. $chr[78] = '111321'; // N
  548. $chr[79] = '121122'; // O
  549. $chr[80] = '131121'; // P
  550. $chr[81] = '212112'; // Q
  551. $chr[82] = '212211'; // R
  552. $chr[83] = '211122'; // S
  553. $chr[84] = '211221'; // T
  554. $chr[85] = '221121'; // U
  555. $chr[86] = '222111'; // V
  556. $chr[87] = '112122'; // W
  557. $chr[88] = '112221'; // X
  558. $chr[89] = '122121'; // Y
  559. $chr[90] = '123111'; // Z
  560. $chr[45] = '121131'; // -
  561. $chr[46] = '311112'; // .
  562. $chr[32] = '311211'; //
  563. $chr[36] = '321111'; // $
  564. $chr[47] = '112131'; // /
  565. $chr[43] = '113121'; // +
  566. $chr[37] = '211131'; // %
  567. $chr[128] = '121221'; // ($)
  568. $chr[129] = '311121'; // (/)
  569. $chr[130] = '122211'; // (+)
  570. $chr[131] = '312111'; // (%)
  571. $chr[42] = '111141'; // start-stop
  572. $code = strtoupper($code);
  573. $encode = array(
  574. chr(0) => chr(131) . 'U',
  575. chr(1) => chr(128) . 'A',
  576. chr(2) => chr(128) . 'B',
  577. chr(3) => chr(128) . 'C',
  578. chr(4) => chr(128) . 'D',
  579. chr(5) => chr(128) . 'E',
  580. chr(6) => chr(128) . 'F',
  581. chr(7) => chr(128) . 'G',
  582. chr(8) => chr(128) . 'H',
  583. chr(9) => chr(128) . 'I',
  584. chr(10) => chr(128) . 'J',
  585. chr(11) => '£K',
  586. chr(12) => chr(128) . 'L',
  587. chr(13) => chr(128) . 'M',
  588. chr(14) => chr(128) . 'N',
  589. chr(15) => chr(128) . 'O',
  590. chr(16) => chr(128) . 'P',
  591. chr(17) => chr(128) . 'Q',
  592. chr(18) => chr(128) . 'R',
  593. chr(19) => chr(128) . 'S',
  594. chr(20) => chr(128) . 'T',
  595. chr(21) => chr(128) . 'U',
  596. chr(22) => chr(128) . 'V',
  597. chr(23) => chr(128) . 'W',
  598. chr(24) => chr(128) . 'X',
  599. chr(25) => chr(128) . 'Y',
  600. chr(26) => chr(128) . 'Z',
  601. chr(27) => chr(131) . 'A',
  602. chr(28) => chr(131) . 'B',
  603. chr(29) => chr(131) . 'C',
  604. chr(30) => chr(131) . 'D',
  605. chr(31) => chr(131) . 'E',
  606. chr(32) => ' ',
  607. chr(33) => chr(129) . 'A',
  608. chr(34) => chr(129) . 'B',
  609. chr(35) => chr(129) . 'C',
  610. chr(36) => chr(129) . 'D',
  611. chr(37) => chr(129) . 'E',
  612. chr(38) => chr(129) . 'F',
  613. chr(39) => chr(129) . 'G',
  614. chr(40) => chr(129) . 'H',
  615. chr(41) => chr(129) . 'I',
  616. chr(42) => chr(129) . 'J',
  617. chr(43) => chr(129) . 'K',
  618. chr(44) => chr(129) . 'L',
  619. chr(45) => '-',
  620. chr(46) => '.',
  621. chr(47) => chr(129) . 'O',
  622. chr(48) => '0',
  623. chr(49) => '1',
  624. chr(50) => '2',
  625. chr(51) => '3',
  626. chr(52) => '4',
  627. chr(53) => '5',
  628. chr(54) => '6',
  629. chr(55) => '7',
  630. chr(56) => '8',
  631. chr(57) => '9',
  632. chr(58) => chr(129) . 'Z',
  633. chr(59) => chr(131) . 'F',
  634. chr(60) => chr(131) . 'G',
  635. chr(61) => chr(131) . 'H',
  636. chr(62) => chr(131) . 'I',
  637. chr(63) => chr(131) . 'J',
  638. chr(64) => chr(131) . 'V',
  639. chr(65) => 'A',
  640. chr(66) => 'B',
  641. chr(67) => 'C',
  642. chr(68) => 'D',
  643. chr(69) => 'E',
  644. chr(70) => 'F',
  645. chr(71) => 'G',
  646. chr(72) => 'H',
  647. chr(73) => 'I',
  648. chr(74) => 'J',
  649. chr(75) => 'K',
  650. chr(76) => 'L',
  651. chr(77) => 'M',
  652. chr(78) => 'N',
  653. chr(79) => 'O',
  654. chr(80) => 'P',
  655. chr(81) => 'Q',
  656. chr(82) => 'R',
  657. chr(83) => 'S',
  658. chr(84) => 'T',
  659. chr(85) => 'U',
  660. chr(86) => 'V',
  661. chr(87) => 'W',
  662. chr(88) => 'X',
  663. chr(89) => 'Y',
  664. chr(90) => 'Z',
  665. chr(91) => chr(131) . 'K',
  666. chr(92) => chr(131) . 'L',
  667. chr(93) => chr(131) . 'M',
  668. chr(94) => chr(131) . 'N',
  669. chr(95) => chr(131) . 'O',
  670. chr(96) => chr(131) . 'W',
  671. chr(97) => chr(130) . 'A',
  672. chr(98) => chr(130) . 'B',
  673. chr(99) => chr(130) . 'C',
  674. chr(100) => chr(130) . 'D',
  675. chr(101) => chr(130) . 'E',
  676. chr(102) => chr(130) . 'F',
  677. chr(103) => chr(130) . 'G',
  678. chr(104) => chr(130) . 'H',
  679. chr(105) => chr(130) . 'I',
  680. chr(106) => chr(130) . 'J',
  681. chr(107) => chr(130) . 'K',
  682. chr(108) => chr(130) . 'L',
  683. chr(109) => chr(130) . 'M',
  684. chr(110) => chr(130) . 'N',
  685. chr(111) => chr(130) . 'O',
  686. chr(112) => chr(130) . 'P',
  687. chr(113) => chr(130) . 'Q',
  688. chr(114) => chr(130) . 'R',
  689. chr(115) => chr(130) . 'S',
  690. chr(116) => chr(130) . 'T',
  691. chr(117) => chr(130) . 'U',
  692. chr(118) => chr(130) . 'V',
  693. chr(119) => chr(130) . 'W',
  694. chr(120) => chr(130) . 'X',
  695. chr(121) => chr(130) . 'Y',
  696. chr(122) => chr(130) . 'Z',
  697. chr(123) => chr(131) . 'P',
  698. chr(124) => chr(131) . 'Q',
  699. chr(125) => chr(131) . 'R',
  700. chr(126) => chr(131) . 'S',
  701. chr(127) => chr(131) . 'T'
  702. );
  703. $code_ext = '';
  704. $clen = strlen($code);
  705. for ($i = 0; $i < $clen; ++$i) {
  706. if (ord($code{$i}) > 127) {
  707. throw new InvalidCharacterException('Only supports till char 127');
  708. }
  709. $code_ext .= $encode[$code{$i}];
  710. }
  711. // checksum
  712. $code_ext .= $this->checksum_code93($code_ext);
  713. // add start and stop codes
  714. $code = '*' . $code_ext . '*';
  715. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  716. $k = 0;
  717. $clen = strlen($code);
  718. for ($i = 0; $i < $clen; ++$i) {
  719. $char = ord($code{$i});
  720. if ( ! isset($chr[$char])) {
  721. throw new InvalidCharacterException('Char ' . $char . ' is unsupported');
  722. }
  723. for ($j = 0; $j < 6; ++$j) {
  724. if (($j % 2) == 0) {
  725. $t = true; // bar
  726. } else {
  727. $t = false; // space
  728. }
  729. $w = $chr[$char]{$j};
  730. $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  731. $bararray['maxw'] += $w;
  732. ++$k;
  733. }
  734. }
  735. $bararray['bcode'][$k] = array('t' => true, 'w' => 1, 'h' => 1, 'p' => 0);
  736. $bararray['maxw'] += 1;
  737. return $bararray;
  738. }
  739. /**
  740. * Calculate CODE 93 checksum (modulo 47).
  741. *
  742. * @param $code (string) code to represent.
  743. * @return string checksum code.
  744. * @protected
  745. */
  746. protected function checksum_code93($code)
  747. {
  748. $chars = array(
  749. '0',
  750. '1',
  751. '2',
  752. '3',
  753. '4',
  754. '5',
  755. '6',
  756. '7',
  757. '8',
  758. '9',
  759. 'A',
  760. 'B',
  761. 'C',
  762. 'D',
  763. 'E',
  764. 'F',
  765. 'G',
  766. 'H',
  767. 'I',
  768. 'J',
  769. 'K',
  770. 'L',
  771. 'M',
  772. 'N',
  773. 'O',
  774. 'P',
  775. 'Q',
  776. 'R',
  777. 'S',
  778. 'T',
  779. 'U',
  780. 'V',
  781. 'W',
  782. 'X',
  783. 'Y',
  784. 'Z',
  785. '-',
  786. '.',
  787. ' ',
  788. '$',
  789. '/',
  790. '+',
  791. '%',
  792. '<',
  793. '=',
  794. '>',
  795. '?'
  796. );
  797. // translate special characters
  798. $code = strtr($code, chr(128) . chr(131) . chr(129) . chr(130), '<=>?');
  799. $len = strlen($code);
  800. // calculate check digit C
  801. $p = 1;
  802. $check = 0;
  803. for ($i = ($len - 1); $i >= 0; --$i) {
  804. $k = array_keys($chars, $code{$i});
  805. $check += ($k[0] * $p);
  806. ++$p;
  807. if ($p > 20) {
  808. $p = 1;
  809. }
  810. }
  811. $check %= 47;
  812. $c = $chars[$check];
  813. $code .= $c;
  814. // calculate check digit K
  815. $p = 1;
  816. $check = 0;
  817. for ($i = $len; $i >= 0; --$i) {
  818. $k = array_keys($chars, $code{$i});
  819. $check += ($k[0] * $p);
  820. ++$p;
  821. if ($p > 15) {
  822. $p = 1;
  823. }
  824. }
  825. $check %= 47;
  826. $k = $chars[$check];
  827. $checksum = $c . $k;
  828. // resto respecial characters
  829. $checksum = strtr($checksum, '<=>?', chr(128) . chr(131) . chr(129) . chr(130));
  830. return $checksum;
  831. }
  832. /**
  833. * Checksum for standard 2 of 5 barcodes.
  834. *
  835. * @param $code (string) code to process.
  836. * @return int checksum.
  837. * @protected
  838. */
  839. protected function checksum_s25($code)
  840. {
  841. $len = strlen($code);
  842. $sum = 0;
  843. for ($i = 0; $i < $len; $i += 2) {
  844. $sum += $code{$i};
  845. }
  846. $sum *= 3;
  847. for ($i = 1; $i < $len; $i += 2) {
  848. $sum += ($code{$i});
  849. }
  850. $r = $sum % 10;
  851. if ($r > 0) {
  852. $r = (10 - $r);
  853. }
  854. return $r;
  855. }
  856. /**
  857. * MSI.
  858. * Variation of Plessey code, with similar applications
  859. * Contains digits (0 to 9) and encodes the data only in the width of bars.
  860. *
  861. * @param $code (string) code to represent.
  862. * @param $checksum (boolean) if true add a checksum to the code (modulo 11)
  863. * @return array barcode representation.
  864. * @protected
  865. */
  866. protected function barcode_msi($code, $checksum = false)
  867. {
  868. $chr['0'] = '100100100100';
  869. $chr['1'] = '100100100110';
  870. $chr['2'] = '100100110100';
  871. $chr['3'] = '100100110110';
  872. $chr['4'] = '100110100100';
  873. $chr['5'] = '100110100110';
  874. $chr['6'] = '100110110100';
  875. $chr['7'] = '100110110110';
  876. $chr['8'] = '110100100100';
  877. $chr['9'] = '110100100110';
  878. $chr['A'] = '110100110100';
  879. $chr['B'] = '110100110110';
  880. $chr['C'] = '110110100100';
  881. $chr['D'] = '110110100110';
  882. $chr['E'] = '110110110100';
  883. $chr['F'] = '110110110110';
  884. if ($checksum) {
  885. // add checksum
  886. $clen = strlen($code);
  887. $p = 2;
  888. $check = 0;
  889. for ($i = ($clen - 1); $i >= 0; --$i) {
  890. $check += (hexdec($code{$i}) * $p);
  891. ++$p;
  892. if ($p > 7) {
  893. $p = 2;
  894. }
  895. }
  896. $check %= 11;
  897. if ($check > 0) {
  898. $check = 11 - $check;
  899. }
  900. $code .= $check;
  901. }
  902. $seq = '110'; // left guard
  903. $clen = strlen($code);
  904. for ($i = 0; $i < $clen; ++$i) {
  905. $digit = $code{$i};
  906. if ( ! isset($chr[$digit])) {
  907. throw new InvalidCharacterException('Char ' . $digit . ' is unsupported');
  908. }
  909. $seq .= $chr[$digit];
  910. }
  911. $seq .= '1001'; // right guard
  912. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  913. return $this->binseq_to_array($seq, $bararray);
  914. }
  915. /**
  916. * Standard 2 of 5 barcodes.
  917. * Used in airline ticket marking, photofinishing
  918. * Contains digits (0 to 9) and encodes the data only in the width of bars.
  919. *
  920. * @param $code (string) code to represent.
  921. * @param $checksum (boolean) if true add a checksum to the code
  922. * @return array barcode representation.
  923. * @protected
  924. */
  925. protected function barcode_s25($code, $checksum = false)
  926. {
  927. $chr['0'] = '10101110111010';
  928. $chr['1'] = '11101010101110';
  929. $chr['2'] = '10111010101110';
  930. $chr['3'] = '11101110101010';
  931. $chr['4'] = '10101110101110';
  932. $chr['5'] = '11101011101010';
  933. $chr['6'] = '10111011101010';
  934. $chr['7'] = '10101011101110';
  935. $chr['8'] = '10101110111010';
  936. $chr['9'] = '10111010111010';
  937. if ($checksum) {
  938. // add checksum
  939. $code .= $this->checksum_s25($code);
  940. }
  941. if ((strlen($code) % 2) != 0) {
  942. // add leading zero if code-length is odd
  943. $code = '0' . $code;
  944. }
  945. $seq = '11011010';
  946. $clen = strlen($code);
  947. for ($i = 0; $i < $clen; ++$i) {
  948. $digit = $code{$i};
  949. if ( ! isset($chr[$digit])) {
  950. throw new InvalidCharacterException('Char ' . $digit . ' is unsupported');
  951. }
  952. $seq .= $chr[$digit];
  953. }
  954. $seq .= '1101011';
  955. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  956. return $this->binseq_to_array($seq, $bararray);
  957. }
  958. /**
  959. * Convert binary barcode sequence to TCPDF barcode array.
  960. *
  961. * @param $seq (string) barcode as binary sequence.
  962. * @param $bararray (array) barcode array.
  963. * òparam array $bararray TCPDF barcode array to fill up
  964. * @return array barcode representation.
  965. * @protected
  966. */
  967. protected function binseq_to_array($seq, $bararray)
  968. {
  969. $len = strlen($seq);
  970. $w = 0;
  971. $k = 0;
  972. for ($i = 0; $i < $len; ++$i) {
  973. $w += 1;
  974. if (($i == ($len - 1)) OR (($i < ($len - 1)) AND ($seq{$i} != $seq{($i + 1)}))) {
  975. if ($seq{$i} == '1') {
  976. $t = true; // bar
  977. } else {
  978. $t = false; // space
  979. }
  980. $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  981. $bararray['maxw'] += $w;
  982. ++$k;
  983. $w = 0;
  984. }
  985. }
  986. return $bararray;
  987. }
  988. /**
  989. * Interleaved 2 of 5 barcodes.
  990. * Compact numeric code, widely used in industry, air cargo
  991. * Contains digits (0 to 9) and encodes the data in the width of both bars and spaces.
  992. *
  993. * @param $code (string) code to represent.
  994. * @param $checksum (boolean) if true add a checksum to the code
  995. * @return array barcode representation.
  996. * @protected
  997. */
  998. protected function barcode_i25($code, $checksum = false)
  999. {
  1000. $chr['0'] = '11221';
  1001. $chr['1'] = '21112';
  1002. $chr['2'] = '12112';
  1003. $chr['3'] = '22111';
  1004. $chr['4'] = '11212';
  1005. $chr['5'] = '21211';
  1006. $chr['6'] = '12211';
  1007. $chr['7'] = '11122';
  1008. $chr['8'] = '21121';
  1009. $chr['9'] = '12121';
  1010. $chr['A'] = '11';
  1011. $chr['Z'] = '21';
  1012. if ($checksum) {
  1013. // add checksum
  1014. $code .= $this->checksum_s25($code);
  1015. }
  1016. if ((strlen($code) % 2) != 0) {
  1017. // add leading zero if code-length is odd
  1018. $code = '0' . $code;
  1019. }
  1020. // add start and stop codes
  1021. $code = 'AA' . strtolower($code) . 'ZA';
  1022. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1023. $k = 0;
  1024. $clen = strlen($code);
  1025. for ($i = 0; $i < $clen; $i = ($i + 2)) {
  1026. $char_bar = $code{$i};
  1027. $char_space = $code{$i + 1};
  1028. if ( ! isset($chr[$char_bar]) || ! isset($chr[$char_space])) {
  1029. throw new InvalidCharacterException();
  1030. }
  1031. // create a bar-space sequence
  1032. $seq = '';
  1033. $chrlen = strlen($chr[$char_bar]);
  1034. for ($s = 0; $s < $chrlen; $s++) {
  1035. $seq .= $chr[$char_bar]{$s} . $chr[$char_space]{$s};
  1036. }
  1037. $seqlen = strlen($seq);
  1038. for ($j = 0; $j < $seqlen; ++$j) {
  1039. if (($j % 2) == 0) {
  1040. $t = true; // bar
  1041. } else {
  1042. $t = false; // space
  1043. }
  1044. $w = $seq{$j};
  1045. $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  1046. $bararray['maxw'] += $w;
  1047. ++$k;
  1048. }
  1049. }
  1050. return $bararray;
  1051. }
  1052. /**
  1053. * C128 barcodes.
  1054. * Very capable code, excellent density, high reliability; in very wide use world-wide
  1055. *
  1056. * @param $code (string) code to represent.
  1057. * @param $type (string) barcode type: A, B, C or empty for automatic switch (AUTO mode)
  1058. * @return array barcode representation.
  1059. * @protected
  1060. */
  1061. protected function barcode_c128($code, $type = '')
  1062. {
  1063. $chr = array(
  1064. '212222', /* 00 */
  1065. '222122', /* 01 */
  1066. '222221', /* 02 */
  1067. '121223', /* 03 */
  1068. '121322', /* 04 */
  1069. '131222', /* 05 */
  1070. '122213', /* 06 */
  1071. '122312', /* 07 */
  1072. '132212', /* 08 */
  1073. '221213', /* 09 */
  1074. '221312', /* 10 */
  1075. '231212', /* 11 */
  1076. '112232', /* 12 */
  1077. '122132', /* 13 */
  1078. '122231', /* 14 */
  1079. '113222', /* 15 */
  1080. '123122', /* 16 */
  1081. '123221', /* 17 */
  1082. '223211', /* 18 */
  1083. '221132', /* 19 */
  1084. '221231', /* 20 */
  1085. '213212', /* 21 */
  1086. '223112', /* 22 */
  1087. '312131', /* 23 */
  1088. '311222', /* 24 */
  1089. '321122', /* 25 */
  1090. '321221', /* 26 */
  1091. '312212', /* 27 */
  1092. '322112', /* 28 */
  1093. '322211', /* 29 */
  1094. '212123', /* 30 */
  1095. '212321', /* 31 */
  1096. '232121', /* 32 */
  1097. '111323', /* 33 */
  1098. '131123', /* 34 */
  1099. '131321', /* 35 */
  1100. '112313', /* 36 */
  1101. '132113', /* 37 */
  1102. '132311', /* 38 */
  1103. '211313', /* 39 */
  1104. '231113', /* 40 */
  1105. '231311', /* 41 */
  1106. '112133', /* 42 */
  1107. '112331', /* 43 */
  1108. '132131', /* 44 */
  1109. '113123', /* 45 */
  1110. '113321', /* 46 */
  1111. '133121', /* 47 */
  1112. '313121', /* 48 */
  1113. '211331', /* 49 */
  1114. '231131', /* 50 */
  1115. '213113', /* 51 */
  1116. '213311', /* 52 */
  1117. '213131', /* 53 */
  1118. '311123', /* 54 */
  1119. '311321', /* 55 */
  1120. '331121', /* 56 */
  1121. '312113', /* 57 */
  1122. '312311', /* 58 */
  1123. '332111', /* 59 */
  1124. '314111', /* 60 */
  1125. '221411', /* 61 */
  1126. '431111', /* 62 */
  1127. '111224', /* 63 */
  1128. '111422', /* 64 */
  1129. '121124', /* 65 */
  1130. '121421', /* 66 */
  1131. '141122', /* 67 */
  1132. '141221', /* 68 */
  1133. '112214', /* 69 */
  1134. '112412', /* 70 */
  1135. '122114', /* 71 */
  1136. '122411', /* 72 */
  1137. '142112', /* 73 */
  1138. '142211', /* 74 */
  1139. '241211', /* 75 */
  1140. '221114', /* 76 */
  1141. '413111', /* 77 */
  1142. '241112', /* 78 */
  1143. '134111', /* 79 */
  1144. '111242', /* 80 */
  1145. '121142', /* 81 */
  1146. '121241', /* 82 */
  1147. '114212', /* 83 */
  1148. '124112', /* 84 */
  1149. '124211', /* 85 */
  1150. '411212', /* 86 */
  1151. '421112', /* 87 */
  1152. '421211', /* 88 */
  1153. '212141', /* 89 */
  1154. '214121', /* 90 */
  1155. '412121', /* 91 */
  1156. '111143', /* 92 */
  1157. '111341', /* 93 */
  1158. '131141', /* 94 */
  1159. '114113', /* 95 */
  1160. '114311', /* 96 */
  1161. '411113', /* 97 */
  1162. '411311', /* 98 */
  1163. '113141', /* 99 */
  1164. '114131', /* 100 */
  1165. '311141', /* 101 */
  1166. '411131', /* 102 */
  1167. '211412', /* 103 START A */
  1168. '211214', /* 104 START B */
  1169. '211232', /* 105 START C */
  1170. '233111', /* STOP */
  1171. '200000' /* END */
  1172. );
  1173. // ASCII characters for code A (ASCII 00 - 95)
  1174. $keys_a = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
  1175. $keys_a .= chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8) . chr(9);
  1176. $keys_a .= chr(10) . chr(11) . chr(12) . chr(13) . chr(14) . chr(15) . chr(16) . chr(17) . chr(18) . chr(19);
  1177. $keys_a .= chr(20) . chr(21) . chr(22) . chr(23) . chr(24) . chr(25) . chr(26) . chr(27) . chr(28) . chr(29);
  1178. $keys_a .= chr(30) . chr(31);
  1179. // ASCII characters for code B (ASCII 32 - 127)
  1180. $keys_b = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~' . chr(127);
  1181. // special codes
  1182. $fnc_a = array(241 => 102, 242 => 97, 243 => 96, 244 => 101);
  1183. $fnc_b = array(241 => 102, 242 => 97, 243 => 96, 244 => 100);
  1184. // array of symbols
  1185. $code_data = array();
  1186. // length of the code
  1187. $len = strlen($code);
  1188. switch (strtoupper($type)) {
  1189. case 'A': { // MODE A
  1190. $startid = 103;
  1191. for ($i = 0; $i < $len; ++$i) {
  1192. $char = $code{$i};
  1193. $char_id = ord($char);
  1194. if (($char_id >= 241) AND ($char_id <= 244)) {
  1195. $code_data[] = $fnc_a[$char_id];
  1196. } elseif (($char_id >= 0) AND ($char_id <= 95)) {
  1197. $code_data[] = strpos($keys_a, $char);
  1198. } else {
  1199. throw new InvalidCharacterException('Char ' . $char . ' is unsupported');
  1200. }
  1201. }
  1202. break;
  1203. }
  1204. case 'B': { // MODE B
  1205. $startid = 104;
  1206. for ($i = 0; $i < $len; ++$i) {
  1207. $char = $code{$i};
  1208. $char_id = ord($char);
  1209. if (($char_id >= 241) AND ($char_id <= 244)) {
  1210. $code_data[] = $fnc_b[$char_id];
  1211. } elseif (($char_id >= 32) AND ($char_id <= 127)) {
  1212. $code_data[] = strpos($keys_b, $char);
  1213. } else {
  1214. throw new InvalidCharacterException('Char ' . $char . ' is unsupported');
  1215. }
  1216. }
  1217. break;
  1218. }
  1219. case 'C': { // MODE C
  1220. $startid = 105;
  1221. if (ord($code[0]) == 241) {
  1222. $code_data[] = 102;
  1223. $code = substr($code, 1);
  1224. --$len;
  1225. }
  1226. if (($len % 2) != 0) {
  1227. throw new InvalidLengthException('Length must be even');
  1228. }
  1229. for ($i = 0; $i < $len; $i += 2) {
  1230. $chrnum = $code{$i} . $code{$i + 1};
  1231. if (preg_match('/([0-9]{2})/', $chrnum) > 0) {
  1232. $code_data[] = intval($chrnum);
  1233. } else {
  1234. throw new InvalidCharacterException();
  1235. }
  1236. }
  1237. break;
  1238. }
  1239. default: { // MODE AUTO
  1240. // split code into sequences
  1241. $sequence = array();
  1242. // get numeric sequences (if any)
  1243. $numseq = array();
  1244. preg_match_all('/([0-9]{4,})/', $code, $numseq, PREG_OFFSET_CAPTURE);
  1245. if (isset($numseq[1]) AND ! empty($numseq[1])) {
  1246. $end_offset = 0;
  1247. foreach ($numseq[1] as $val) {
  1248. $offset = $val[1];
  1249. if ($offset > $end_offset) {
  1250. // non numeric sequence
  1251. $sequence = array_merge($sequence,
  1252. $this->get128ABsequence(substr($code, $end_offset, ($offset - $end_offset))));
  1253. }
  1254. // numeric sequence
  1255. $slen = strlen($val[0]);
  1256. if (($slen % 2) != 0) {
  1257. // the length must be even
  1258. --$slen;
  1259. }
  1260. $sequence[] = array('C', substr($code, $offset, $slen), $slen);
  1261. $end_offset = $offset + $slen;
  1262. }
  1263. if ($end_offset < $len) {
  1264. $sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset)));
  1265. }
  1266. } else {
  1267. // text code (non C mode)
  1268. $sequence = array_merge($sequence, $this->get128ABsequence($code));
  1269. }
  1270. // process the sequence
  1271. foreach ($sequence as $key => $seq) {
  1272. switch ($seq[0]) {
  1273. case 'A': {
  1274. if ($key == 0) {
  1275. $startid = 103;
  1276. } elseif ($sequence[($key - 1)][0] != 'A') {
  1277. if (($seq[2] == 1) AND ($key > 0) AND ($sequence[($key - 1)][0] == 'B') AND ( ! isset($sequence[($key - 1)][3]))) {
  1278. // single character shift
  1279. $code_data[] = 98;
  1280. // mark shift
  1281. $sequence[$key][3] = true;
  1282. } elseif ( ! isset($sequence[($key - 1)][3])) {
  1283. $code_data[] = 101;
  1284. }
  1285. }
  1286. for ($i = 0; $i < $seq[2]; ++$i) {
  1287. $char = $seq[1]{$i};
  1288. $char_id = ord($char);
  1289. if (($char_id >= 241) AND ($char_id <= 244)) {
  1290. $code_data[] = $fnc_a[$char_id];
  1291. } else {
  1292. $code_data[] = strpos($keys_a, $char);
  1293. }
  1294. }
  1295. break;
  1296. }
  1297. case 'B': {
  1298. if ($key == 0) {
  1299. $tmpchr = ord($seq[1][0]);
  1300. if (($seq[2] == 1) AND ($tmpchr >= 241) AND ($tmpchr <= 244) AND isset($sequence[($key + 1)]) AND ($sequence[($key + 1)][0] != 'B')) {
  1301. switch ($sequence[($key + 1)][0]) {
  1302. case 'A': {
  1303. $startid = 103;
  1304. $sequence[$key][0] = 'A';
  1305. $code_data[] = $fnc_a[$tmpchr];
  1306. break;
  1307. }
  1308. case 'C': {
  1309. $startid = 105;
  1310. $sequence[$key][0] = 'C';
  1311. $code_data[] = $fnc_a[$tmpchr];
  1312. break;
  1313. }
  1314. }
  1315. break;
  1316. } else {
  1317. $startid = 104;
  1318. }
  1319. } elseif ($sequence[($key - 1)][0] != 'B') {
  1320. if (($seq[2] == 1) AND ($key > 0) AND ($sequence[($key - 1)][0] == 'A') AND ( ! isset($sequence[($key - 1)][3]))) {
  1321. // single character shift
  1322. $code_data[] = 98;
  1323. // mark shift
  1324. $sequence[$key][3] = true;
  1325. } elseif ( ! isset($sequence[($key - 1)][3])) {
  1326. $code_data[] = 100;
  1327. }
  1328. }
  1329. for ($i = 0; $i < $seq[2]; ++$i) {
  1330. $char = $seq[1]{$i};
  1331. $char_id = ord($char);
  1332. if (($char_id >= 241) AND ($char_id <= 244)) {
  1333. $code_data[] = $fnc_b[$char_id];
  1334. } else {
  1335. $code_data[] = strpos($keys_b, $char);
  1336. }
  1337. }
  1338. break;
  1339. }
  1340. case 'C': {
  1341. if ($key == 0) {
  1342. $startid = 105;
  1343. } elseif ($sequence[($key - 1)][0] != 'C') {
  1344. $code_data[] = 99;
  1345. }
  1346. for ($i = 0; $i < $seq[2]; $i += 2) {
  1347. $chrnum = $seq[1]{$i} . $seq[1]{$i + 1};
  1348. $code_data[] = intval($chrnum);
  1349. }
  1350. break;
  1351. }
  1352. }
  1353. }
  1354. }
  1355. }
  1356. // calculate check character
  1357. $sum = $startid;
  1358. foreach ($code_data as $key => $val) {
  1359. $sum += ($val * ($key + 1));
  1360. }
  1361. // add check character
  1362. $code_data[] = ($sum % 103);
  1363. // add stop sequence
  1364. $code_data[] = 106;
  1365. $code_data[] = 107;
  1366. // add start code at the beginning
  1367. array_unshift($code_data, $startid);
  1368. // build barcode array
  1369. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1370. foreach ($code_data as $val) {
  1371. $seq = $chr[$val];
  1372. for ($j = 0; $j < 6; ++$j) {
  1373. if (($j % 2) == 0) {
  1374. $t = true; // bar
  1375. } else {
  1376. $t = false; // space
  1377. }
  1378. $w = $seq{$j};
  1379. $bararray['bcode'][] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  1380. $bararray['maxw'] += $w;
  1381. }
  1382. }
  1383. return $bararray;
  1384. }
  1385. /**
  1386. * Split text code in A/B sequence for 128 code
  1387. *
  1388. * @param $code (string) code to split.
  1389. * @return array sequence
  1390. * @protected
  1391. */
  1392. protected function get128ABsequence($code)
  1393. {
  1394. $len = strlen($code);
  1395. $sequence = array();
  1396. // get A sequences (if any)
  1397. $numseq = array();
  1398. preg_match_all('/([\x00-\x1f])/', $code, $numseq, PREG_OFFSET_CAPTURE);
  1399. if (isset($numseq[1]) AND ! empty($numseq[1])) {
  1400. $end_offset = 0;
  1401. foreach ($numseq[1] as $val) {
  1402. $offset = $val[1];
  1403. if ($offset > $end_offset) {
  1404. // B sequence
  1405. $sequence[] = array(
  1406. 'B',
  1407. substr($code, $end_offset, ($offset - $end_offset)),
  1408. ($offset - $end_offset)
  1409. );
  1410. }
  1411. // A sequence
  1412. $slen = strlen($val[0]);
  1413. $sequence[] = array('A', substr($code, $offset, $slen), $slen);
  1414. $end_offset = $offset + $slen;
  1415. }
  1416. if ($end_offset < $len) {
  1417. $sequence[] = array('B', substr($code, $end_offset), ($len - $end_offset));
  1418. }
  1419. } else {
  1420. // only B sequence
  1421. $sequence[] = array('B', $code, $len);
  1422. }
  1423. return $sequence;
  1424. }
  1425. /**
  1426. * EAN13 and UPC-A barcodes.
  1427. * EAN13: European Article Numbering international retail product code
  1428. * UPC-A: Universal product code seen on almost all retail products in the USA and Canada
  1429. * UPC-E: Short version of UPC symbol
  1430. *
  1431. * @param $code (string) code to represent.
  1432. * @param $len (string) barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
  1433. * @return array barcode representation.
  1434. * @protected
  1435. */
  1436. protected function barcode_eanupc($code, $len = 13)
  1437. {
  1438. $upce = false;
  1439. if ($len == 6) {
  1440. $len = 12; // UPC-A
  1441. $upce = true; // UPC-E mode
  1442. }
  1443. $data_len = $len - 1;
  1444. //Padding
  1445. $code = str_pad($code, $data_len, '0', STR_PAD_LEFT);
  1446. $code_len = strlen($code);
  1447. // calculate check digit
  1448. $sum_a = 0;
  1449. for ($i = 1; $i < $data_len; $i += 2) {
  1450. $sum_a += $code{$i};
  1451. }
  1452. if ($len > 12) {
  1453. $sum_a *= 3;
  1454. }
  1455. $sum_b = 0;
  1456. for ($i = 0; $i < $data_len; $i += 2) {
  1457. $sum_b += ($code{$i});
  1458. }
  1459. if ($len < 13) {
  1460. $sum_b *= 3;
  1461. }
  1462. $r = ($sum_a + $sum_b) % 10;
  1463. if ($r > 0) {
  1464. $r = (10 - $r);
  1465. }
  1466. if ($code_len == $data_len) {
  1467. // add check digit
  1468. $code .= $r;
  1469. } elseif ($r !== intval($code{$data_len})) {
  1470. throw new InvalidCheckDigitException();
  1471. }
  1472. if ($len == 12) {
  1473. // UPC-A
  1474. $code = '0' . $code;
  1475. ++$len;
  1476. }
  1477. if ($upce) {
  1478. // convert UPC-A to UPC-E
  1479. $tmp = substr($code, 4, 3);
  1480. if (($tmp == '000') OR ($tmp == '100') OR ($tmp == '200')) {
  1481. // manufacturer code ends in 000, 100, or 200
  1482. $upce_code = substr($code, 2, 2) . substr($code, 9, 3) . substr($code, 4, 1);
  1483. } else {
  1484. $tmp = substr($code, 5, 2);
  1485. if ($tmp == '00') {
  1486. // manufacturer code ends in 00
  1487. $upce_code = substr($code, 2, 3) . substr($code, 10, 2) . '3';
  1488. } else {
  1489. $tmp = substr($code, 6, 1);
  1490. if ($tmp == '0') {
  1491. // manufacturer code ends in 0
  1492. $upce_code = substr($code, 2, 4) . substr($code, 11, 1) . '4';
  1493. } else {
  1494. // manufacturer code does not end in zero
  1495. $upce_code = substr($code, 2, 5) . substr($code, 11, 1);
  1496. }
  1497. }
  1498. }
  1499. }
  1500. //Convert digits to bars
  1501. $codes = array(
  1502. 'A' => array( // left odd parity
  1503. '0' => '0001101',
  1504. '1' => '0011001',
  1505. '2' => '0010011',
  1506. '3' => '0111101',
  1507. '4' => '0100011',
  1508. '5' => '0110001',
  1509. '6' => '0101111',
  1510. '7' => '0111011',
  1511. '8' => '0110111',
  1512. '9' => '0001011'
  1513. ),
  1514. 'B' => array( // left even parity
  1515. '0' => '0100111',
  1516. '1' => '0110011',
  1517. '2' => '0011011',
  1518. '3' => '0100001',
  1519. '4' => '0011101',
  1520. '5' => '0111001',
  1521. '6' => '0000101',
  1522. '7' => '0010001',
  1523. '8' => '0001001',
  1524. '9' => '0010111'
  1525. ),
  1526. 'C' => array( // right
  1527. '0' => '1110010',
  1528. '1' => '1100110',
  1529. '2' => '1101100',
  1530. '3' => '1000010',
  1531. '4' => '1011100',
  1532. '5' => '1001110',
  1533. '6' => '1010000',
  1534. '7' => '1000100',
  1535. '8' => '1001000',
  1536. '9' => '1110100'
  1537. )
  1538. );
  1539. $parities = array(
  1540. '0' => array('A', 'A', 'A', 'A', 'A', 'A'),
  1541. '1' => array('A', 'A', 'B', 'A', 'B', 'B'),
  1542. '2' => array('A', 'A', 'B', 'B', 'A', 'B'),
  1543. '3' => array('A', 'A', 'B', 'B', 'B', 'A'),
  1544. '4' => array('A', 'B', 'A', 'A', 'B', 'B'),
  1545. '5' => array('A', 'B', 'B', 'A', 'A', 'B'),
  1546. '6' => array('A', 'B', 'B', 'B', 'A', 'A'),
  1547. '7' => array('A', 'B', 'A', 'B', 'A', 'B'),
  1548. '8' => array('A', 'B', 'A', 'B', 'B', 'A'),
  1549. '9' => array('A', 'B', 'B', 'A', 'B', 'A')
  1550. );
  1551. $upce_parities = array();
  1552. $upce_parities[0] = array(
  1553. '0' => array('B', 'B', 'B', 'A', 'A', 'A'),
  1554. '1' => array('B', 'B', 'A', 'B', 'A', 'A'),
  1555. '2' => array('B', 'B', 'A', 'A', 'B', 'A'),
  1556. '3' => array('B', 'B', 'A', 'A', 'A', 'B'),
  1557. '4' => array('B', 'A', 'B', 'B', 'A', 'A'),
  1558. '5' => array('B', 'A', 'A', 'B', 'B', 'A'),
  1559. '6' => array('B', 'A', 'A', 'A', 'B', 'B'),
  1560. '7' => array('B', 'A', 'B', 'A', 'B', 'A'),
  1561. '8' => array('B', 'A', 'B', 'A', 'A', 'B'),
  1562. '9' => array('B', 'A', 'A', 'B', 'A', 'B')
  1563. );
  1564. $upce_parities[1] = array(
  1565. '0' => array('A', 'A', 'A', 'B', 'B', 'B'),
  1566. '1' => array('A', 'A', 'B', 'A', 'B', 'B'),
  1567. '2' => array('A', 'A', 'B', 'B', 'A', 'B'),
  1568. '3' => array('A', 'A', 'B', 'B', 'B', 'A'),
  1569. '4' => array('A', 'B', 'A', 'A', 'B', 'B'),
  1570. '5' => array('A', 'B', 'B', 'A', 'A', 'B'),
  1571. '6' => array('A', 'B', 'B', 'B', 'A', 'A'),
  1572. '7' => array('A', 'B', 'A', 'B', 'A', 'B'),
  1573. '8' => array('A', 'B', 'A', 'B', 'B', 'A'),
  1574. '9' => array('A', 'B', 'B', 'A', 'B', 'A')
  1575. );
  1576. $k = 0;
  1577. $seq = '101'; // left guard bar
  1578. if ($upce) {
  1579. $bararray = array('code' => $upce_code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1580. $p = $upce_parities[$code[1]][$r];
  1581. for ($i = 0; $i < 6; ++$i) {
  1582. $seq .= $codes[$p[$i]][$upce_code{$i}];
  1583. }
  1584. $seq .= '010101'; // right guard bar
  1585. } else {
  1586. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1587. $half_len = intval(ceil($len / 2));
  1588. if ($len == 8) {
  1589. for ($i = 0; $i < $half_len; ++$i) {
  1590. $seq .= $codes['A'][$code{$i}];
  1591. }
  1592. } else {
  1593. $p = $parities[$code[0]];
  1594. for ($i = 1; $i < $half_len; ++$i) {
  1595. $seq .= $codes[$p[$i - 1]][$code{$i}];
  1596. }
  1597. }
  1598. $seq .= '01010'; // center guard bar
  1599. for ($i = $half_len; $i < $len; ++$i) {
  1600. if ( ! isset($codes['C'][$code{$i}])) {
  1601. throw new InvalidCharacterException('Char ' . $code{$i} . ' not allowed');
  1602. }
  1603. $seq .= $codes['C'][$code{$i}];
  1604. }
  1605. $seq .= '101'; // right guard bar
  1606. }
  1607. $clen = strlen($seq);
  1608. $w = 0;
  1609. for ($i = 0; $i < $clen; ++$i) {
  1610. $w += 1;
  1611. if (($i == ($clen - 1)) OR (($i < ($clen - 1)) AND ($seq{$i} != $seq{($i + 1)}))) {
  1612. if ($seq{$i} == '1') {
  1613. $t = true; // bar
  1614. } else {
  1615. $t = false; // space
  1616. }
  1617. $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  1618. $bararray['maxw'] += $w;
  1619. ++$k;
  1620. $w = 0;
  1621. }
  1622. }
  1623. return $bararray;
  1624. }
  1625. /**
  1626. * UPC-Based Extensions
  1627. * 2-Digit Ext.: Used to indicate magazines and newspaper issue numbers
  1628. * 5-Digit Ext.: Used to mark suggested retail price of books
  1629. *
  1630. * @param $code (string) code to represent.
  1631. * @param $len (string) barcode type: 2 = 2-Digit, 5 = 5-Digit
  1632. * @return array barcode representation.
  1633. * @protected
  1634. */
  1635. protected function barcode_eanext($code, $len = 5)
  1636. {
  1637. //Padding
  1638. $code = str_pad($code, $len, '0', STR_PAD_LEFT);
  1639. // calculate check digit
  1640. if ($len == 2) {
  1641. $r = $code % 4;
  1642. } elseif ($len == 5) {
  1643. $r = (3 * ($code[0] + $code[2] + $code[4])) + (9 * ($code[1] + $code[3]));
  1644. $r %= 10;
  1645. } else {
  1646. throw new InvalidCheckDigitException();
  1647. }
  1648. //Convert digits to bars
  1649. $codes = array(
  1650. 'A' => array( // left odd parity
  1651. '0' => '0001101',
  1652. '1' => '0011001',
  1653. '2' => '0010011',
  1654. '3' => '0111101',
  1655. '4' => '0100011',
  1656. '5' => '0110001',
  1657. '6' => '0101111',
  1658. '7' => '0111011',
  1659. '8' => '0110111',
  1660. '9' => '0001011'
  1661. ),
  1662. 'B' => array( // left even parity
  1663. '0' => '0100111',
  1664. '1' => '0110011',
  1665. '2' => '0011011',
  1666. '3' => '0100001',
  1667. '4' => '0011101',
  1668. '5' => '0111001',
  1669. '6' => '0000101',
  1670. '7' => '0010001',
  1671. '8' => '0001001',
  1672. '9' => '0010111'
  1673. )
  1674. );
  1675. $parities = array();
  1676. $parities[2] = array(
  1677. '0' => array('A', 'A'),
  1678. '1' => array('A', 'B'),
  1679. '2' => array('B', 'A'),
  1680. '3' => array('B', 'B')
  1681. );
  1682. $parities[5] = array(
  1683. '0' => array('B', 'B', 'A', 'A', 'A'),
  1684. '1' => array('B', 'A', 'B', 'A', 'A'),
  1685. '2' => array('B', 'A', 'A', 'B', 'A'),
  1686. '3' => array('B', 'A', 'A', 'A', 'B'),
  1687. '4' => array('A', 'B', 'B', 'A', 'A'),
  1688. '5' => array('A', 'A', 'B', 'B', 'A'),
  1689. '6' => array('A', 'A', 'A', 'B', 'B'),
  1690. '7' => array('A', 'B', 'A', 'B', 'A'),
  1691. '8' => array('A', 'B', 'A', 'A', 'B'),
  1692. '9' => array('A', 'A', 'B', 'A', 'B')
  1693. );
  1694. $p = $parities[$len][$r];
  1695. $seq = '1011'; // left guard bar
  1696. $seq .= $codes[$p[0]][$code[0]];
  1697. for ($i = 1; $i < $len; ++$i) {
  1698. $seq .= '01'; // separator
  1699. $seq .= $codes[$p[$i]][$code{$i}];
  1700. }
  1701. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1702. return $this->binseq_to_array($seq, $bararray);
  1703. }
  1704. /**
  1705. * POSTNET and PLANET barcodes.
  1706. * Used by U.S. Postal Service for automated mail sorting
  1707. *
  1708. * @param $code (string) zip code to represent. Must be a string containing a zip code of the form DDDDD or
  1709. * DDDDD-DDDD.
  1710. * @param $planet (boolean) if true print the PLANET barcode, otherwise print POSTNET
  1711. * @return array barcode representation.
  1712. * @protected
  1713. */
  1714. protected function barcode_postnet($code, $planet = false)
  1715. {
  1716. // bar length
  1717. if ($planet) {
  1718. $barlen = Array(
  1719. 0 => Array(1, 1, 2, 2, 2),
  1720. 1 => Array(2, 2, 2, 1, 1),
  1721. 2 => Array(2, 2, 1, 2, 1),
  1722. 3 => Array(2, 2, 1, 1, 2),
  1723. 4 => Array(2, 1, 2, 2, 1),
  1724. 5 => Array(2, 1, 2, 1, 2),
  1725. 6 => Array(2, 1, 1, 2, 2),
  1726. 7 => Array(1, 2, 2, 2, 1),
  1727. 8 => Array(1, 2, 2, 1, 2),
  1728. 9 => Array(1, 2, 1, 2, 2)
  1729. );
  1730. } else {
  1731. $barlen = Array(
  1732. 0 => Array(2, 2, 1, 1, 1),
  1733. 1 => Array(1, 1, 1, 2, 2),
  1734. 2 => Array(1, 1, 2, 1, 2),
  1735. 3 => Array(1, 1, 2, 2, 1),
  1736. 4 => Array(1, 2, 1, 1, 2),
  1737. 5 => Array(1, 2, 1, 2, 1),
  1738. 6 => Array(1, 2, 2, 1, 1),
  1739. 7 => Array(2, 1, 1, 1, 2),
  1740. 8 => Array(2, 1, 1, 2, 1),
  1741. 9 => Array(2, 1, 2, 1, 1)
  1742. );
  1743. }
  1744. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array());
  1745. $k = 0;
  1746. $code = str_replace('-', '', $code);
  1747. $code = str_replace(' ', '', $code);
  1748. $len = strlen($code);
  1749. // calculate checksum
  1750. $sum = 0;
  1751. for ($i = 0; $i < $len; ++$i) {
  1752. $sum += intval($code{$i});
  1753. }
  1754. $chkd = ($sum % 10);
  1755. if ($chkd > 0) {
  1756. $chkd = (10 - $chkd);
  1757. }
  1758. $code .= $chkd;
  1759. $len = strlen($code);
  1760. // start bar
  1761. $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
  1762. $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  1763. $bararray['maxw'] += 2;
  1764. for ($i = 0; $i < $len; ++$i) {
  1765. for ($j = 0; $j < 5; ++$j) {
  1766. $h = $barlen[$code{$i}][$j];
  1767. $p = floor(1 / $h);
  1768. $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
  1769. $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  1770. $bararray['maxw'] += 2;
  1771. }
  1772. }
  1773. // end bar
  1774. $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
  1775. $bararray['maxw'] += 1;
  1776. return $bararray;
  1777. }
  1778. /**
  1779. * RMS4CC - CBC - KIX
  1780. * RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) - KIX (Klant index - Customer index)
  1781. * RM4SCC is the name of the barcode symbology used by the Royal Mail for its Cleanmail service.
  1782. *
  1783. * @param $code (string) code to print
  1784. * @param $kix (boolean) if true prints the KIX variation (doesn't use the start and end symbols, and the checksum)
  1785. * - in this case the house number must be sufficed with an X and placed at the end of the code.
  1786. * @return array barcode representation.
  1787. * @protected
  1788. */
  1789. protected function barcode_rms4cc($code, $kix = false)
  1790. {
  1791. $notkix = ! $kix;
  1792. // bar mode
  1793. // 1 = pos 1, length 2
  1794. // 2 = pos 1, length 3
  1795. // 3 = pos 2, length 1
  1796. // 4 = pos 2, length 2
  1797. $barmode = array(
  1798. '0' => array(3, 3, 2, 2),
  1799. '1' => array(3, 4, 1, 2),
  1800. '2' => array(3, 4, 2, 1),
  1801. '3' => array(4, 3, 1, 2),
  1802. '4' => array(4, 3, 2, 1),
  1803. '5' => array(4, 4, 1, 1),
  1804. '6' => array(3, 1, 4, 2),
  1805. '7' => array(3, 2, 3, 2),
  1806. '8' => array(3, 2, 4, 1),
  1807. '9' => array(4, 1, 3, 2),
  1808. 'A' => array(4, 1, 4, 1),
  1809. 'B' => array(4, 2, 3, 1),
  1810. 'C' => array(3, 1, 2, 4),
  1811. 'D' => array(3, 2, 1, 4),
  1812. 'E' => array(3, 2, 2, 3),
  1813. 'F' => array(4, 1, 1, 4),
  1814. 'G' => array(4, 1, 2, 3),
  1815. 'H' => array(4, 2, 1, 3),
  1816. 'I' => array(1, 3, 4, 2),
  1817. 'J' => array(1, 4, 3, 2),
  1818. 'K' => array(1, 4, 4, 1),
  1819. 'L' => array(2, 3, 3, 2),
  1820. 'M' => array(2, 3, 4, 1),
  1821. 'N' => array(2, 4, 3, 1),
  1822. 'O' => array(1, 3, 2, 4),
  1823. 'P' => array(1, 4, 1, 4),
  1824. 'Q' => array(1, 4, 2, 3),
  1825. 'R' => array(2, 3, 1, 4),
  1826. 'S' => array(2, 3, 2, 3),
  1827. 'T' => array(2, 4, 1, 3),
  1828. 'U' => array(1, 1, 4, 4),
  1829. 'V' => array(1, 2, 3, 4),
  1830. 'W' => array(1, 2, 4, 3),
  1831. 'X' => array(2, 1, 3, 4),
  1832. 'Y' => array(2, 1, 4, 3),
  1833. 'Z' => array(2, 2, 3, 3)
  1834. );
  1835. $code = strtoupper($code);
  1836. $len = strlen($code);
  1837. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
  1838. if ($notkix) {
  1839. // table for checksum calculation (row,col)
  1840. $checktable = array(
  1841. '0' => array(1, 1),
  1842. '1' => array(1, 2),
  1843. '2' => array(1, 3),
  1844. '3' => array(1, 4),
  1845. '4' => array(1, 5),
  1846. '5' => array(1, 0),
  1847. '6' => array(2, 1),
  1848. '7' => array(2, 2),
  1849. '8' => array(2, 3),
  1850. '9' => array(2, 4),
  1851. 'A' => array(2, 5),
  1852. 'B' => array(2, 0),
  1853. 'C' => array(3, 1),
  1854. 'D' => array(3, 2),
  1855. 'E' => array(3, 3),
  1856. 'F' => array(3, 4),
  1857. 'G' => array(3, 5),
  1858. 'H' => array(3, 0),
  1859. 'I' => array(4, 1),
  1860. 'J' => array(4, 2),
  1861. 'K' => array(4, 3),
  1862. 'L' => array(4, 4),
  1863. 'M' => array(4, 5),
  1864. 'N' => array(4, 0),
  1865. 'O' => array(5, 1),
  1866. 'P' => array(5, 2),
  1867. 'Q' => array(5, 3),
  1868. 'R' => array(5, 4),
  1869. 'S' => array(5, 5),
  1870. 'T' => array(5, 0),
  1871. 'U' => array(0, 1),
  1872. 'V' => array(0, 2),
  1873. 'W' => array(0, 3),
  1874. 'X' => array(0, 4),
  1875. 'Y' => array(0, 5),
  1876. 'Z' => array(0, 0)
  1877. );
  1878. $row = 0;
  1879. $col = 0;
  1880. for ($i = 0; $i < $len; ++$i) {
  1881. $row += $checktable[$code{$i}][0];
  1882. $col += $checktable[$code{$i}][1];
  1883. }
  1884. $row %= 6;
  1885. $col %= 6;
  1886. $chk = array_keys($checktable, array($row, $col));
  1887. $code .= $chk[0];
  1888. ++$len;
  1889. }
  1890. $k = 0;
  1891. if ($notkix) {
  1892. // start bar
  1893. $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
  1894. $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  1895. $bararray['maxw'] += 2;
  1896. }
  1897. for ($i = 0; $i < $len; ++$i) {
  1898. for ($j = 0; $j < 4; ++$j) {
  1899. switch ($barmode[$code{$i}][$j]) {
  1900. case 1: {
  1901. $p = 0;
  1902. $h = 2;
  1903. break;
  1904. }
  1905. case 2: {
  1906. $p = 0;
  1907. $h = 3;
  1908. break;
  1909. }
  1910. case 3: {
  1911. $p = 1;
  1912. $h = 1;
  1913. break;
  1914. }
  1915. case 4: {
  1916. $p = 1;
  1917. $h = 2;
  1918. break;
  1919. }
  1920. }
  1921. $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
  1922. $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  1923. $bararray['maxw'] += 2;
  1924. }
  1925. }
  1926. if ($notkix) {
  1927. // stop bar
  1928. $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 3, 'p' => 0);
  1929. $bararray['maxw'] += 1;
  1930. }
  1931. return $bararray;
  1932. }
  1933. /**
  1934. * CODABAR barcodes.
  1935. * Older code often used in library systems, sometimes in blood banks
  1936. *
  1937. * @param $code (string) code to represent.
  1938. * @return array barcode representation.
  1939. * @protected
  1940. */
  1941. protected function barcode_codabar($code)
  1942. {
  1943. $chr = array(
  1944. '0' => '11111221',
  1945. '1' => '11112211',
  1946. '2' => '11121121',
  1947. '3' => '22111111',
  1948. '4' => '11211211',
  1949. '5' => '21111211',
  1950. '6' => '12111121',
  1951. '7' => '12112111',
  1952. '8' => '12211111',
  1953. '9' => '21121111',
  1954. '-' => '11122111',
  1955. '$' => '11221111',
  1956. ':' => '21112121',
  1957. '/' => '21211121',
  1958. '.' => '21212111',
  1959. '+' => '11222221',
  1960. 'A' => '11221211',
  1961. 'B' => '12121121',
  1962. 'C' => '11121221',
  1963. 'D' => '11122211'
  1964. );
  1965. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  1966. $k = 0;
  1967. $w = 0;
  1968. $seq = '';
  1969. $code = 'A' . strtoupper($code) . 'A';
  1970. $len = strlen($code);
  1971. for ($i = 0; $i < $len; ++$i) {
  1972. if ( ! isset($chr[$code{$i}])) {
  1973. throw new InvalidCharacterException('Char ' . $code{$i} . ' is unsupported');
  1974. }
  1975. $seq = $chr[$code{$i}];
  1976. for ($j = 0; $j < 8; ++$j) {
  1977. if (($j % 2) == 0) {
  1978. $t = true; // bar
  1979. } else {
  1980. $t = false; // space
  1981. }
  1982. $w = $seq{$j};
  1983. $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  1984. $bararray['maxw'] += $w;
  1985. ++$k;
  1986. }
  1987. }
  1988. return $bararray;
  1989. }
  1990. /**
  1991. * CODE11 barcodes.
  1992. * Used primarily for labeling telecommunications equipment
  1993. *
  1994. * @param $code (string) code to represent.
  1995. * @return array barcode representation.
  1996. * @protected
  1997. */
  1998. protected function barcode_code11($code)
  1999. {
  2000. $chr = array(
  2001. '0' => '111121',
  2002. '1' => '211121',
  2003. '2' => '121121',
  2004. '3' => '221111',
  2005. '4' => '112121',
  2006. '5' => '212111',
  2007. '6' => '122111',
  2008. '7' => '111221',
  2009. '8' => '211211',
  2010. '9' => '211111',
  2011. '-' => '112111',
  2012. 'S' => '112211'
  2013. );
  2014. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  2015. $k = 0;
  2016. $w = 0;
  2017. $seq = '';
  2018. $len = strlen($code);
  2019. // calculate check digit C
  2020. $p = 1;
  2021. $check = 0;
  2022. for ($i = ($len - 1); $i >= 0; --$i) {
  2023. $digit = $code{$i};
  2024. if ($digit == '-') {
  2025. $dval = 10;
  2026. } else {
  2027. $dval = intval($digit);
  2028. }
  2029. $check += ($dval * $p);
  2030. ++$p;
  2031. if ($p > 10) {
  2032. $p = 1;
  2033. }
  2034. }
  2035. $check %= 11;
  2036. if ($check == 10) {
  2037. $check = '-';
  2038. }
  2039. $code .= $check;
  2040. if ($len > 10) {
  2041. // calculate check digit K
  2042. $p = 1;
  2043. $check = 0;
  2044. for ($i = $len; $i >= 0; --$i) {
  2045. $digit = $code{$i};
  2046. if ($digit == '-') {
  2047. $dval = 10;
  2048. } else {
  2049. $dval = intval($digit);
  2050. }
  2051. $check += ($dval * $p);
  2052. ++$p;
  2053. if ($p > 9) {
  2054. $p = 1;
  2055. }
  2056. }
  2057. $check %= 11;
  2058. $code .= $check;
  2059. ++$len;
  2060. }
  2061. $code = 'S' . $code . 'S';
  2062. $len += 3;
  2063. for ($i = 0; $i < $len; ++$i) {
  2064. if ( ! isset($chr[$code{$i}])) {
  2065. throw new InvalidCharacterException('Char ' . $code{$i} . ' is unsupported');
  2066. }
  2067. $seq = $chr[$code{$i}];
  2068. for ($j = 0; $j < 6; ++$j) {
  2069. if (($j % 2) == 0) {
  2070. $t = true; // bar
  2071. } else {
  2072. $t = false; // space
  2073. }
  2074. $w = $seq{$j};
  2075. $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
  2076. $bararray['maxw'] += $w;
  2077. ++$k;
  2078. }
  2079. }
  2080. return $bararray;
  2081. }
  2082. /**
  2083. * Pharmacode
  2084. * Contains digits (0 to 9)
  2085. *
  2086. * @param $code (string) code to represent.
  2087. * @return array barcode representation.
  2088. * @protected
  2089. */
  2090. protected function barcode_pharmacode($code)
  2091. {
  2092. $seq = '';
  2093. $code = intval($code);
  2094. while ($code > 0) {
  2095. if (($code % 2) == 0) {
  2096. $seq .= '11100';
  2097. $code -= 2;
  2098. } else {
  2099. $seq .= '100';
  2100. $code -= 1;
  2101. }
  2102. $code /= 2;
  2103. }
  2104. $seq = substr($seq, 0, -2);
  2105. $seq = strrev($seq);
  2106. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
  2107. return $this->binseq_to_array($seq, $bararray);
  2108. }
  2109. /**
  2110. * Pharmacode two-track
  2111. * Contains digits (0 to 9)
  2112. *
  2113. * @param $code (string) code to represent.
  2114. * @return array barcode representation.
  2115. * @protected
  2116. */
  2117. protected function barcode_pharmacode2t($code)
  2118. {
  2119. $seq = '';
  2120. $code = intval($code);
  2121. do {
  2122. switch ($code % 3) {
  2123. case 0: {
  2124. $seq .= '3';
  2125. $code = ($code - 3) / 3;
  2126. break;
  2127. }
  2128. case 1: {
  2129. $seq .= '1';
  2130. $code = ($code - 1) / 3;
  2131. break;
  2132. }
  2133. case 2: {
  2134. $seq .= '2';
  2135. $code = ($code - 2) / 3;
  2136. break;
  2137. }
  2138. }
  2139. } while ($code != 0);
  2140. $seq = strrev($seq);
  2141. $k = 0;
  2142. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array());
  2143. $len = strlen($seq);
  2144. for ($i = 0; $i < $len; ++$i) {
  2145. switch ($seq{$i}) {
  2146. case '1': {
  2147. $p = 1;
  2148. $h = 1;
  2149. break;
  2150. }
  2151. case '2': {
  2152. $p = 0;
  2153. $h = 1;
  2154. break;
  2155. }
  2156. case '3': {
  2157. $p = 0;
  2158. $h = 2;
  2159. break;
  2160. }
  2161. }
  2162. $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
  2163. $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  2164. $bararray['maxw'] += 2;
  2165. }
  2166. unset($bararray['bcode'][($k - 1)]);
  2167. --$bararray['maxw'];
  2168. return $bararray;
  2169. }
  2170. /**
  2171. * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
  2172. * (requires PHP bcmath extension)
  2173. * Intelligent Mail barcode is a 65-bar code for use on mail in the United States.
  2174. * The fields are described as follows:<ul><li>The Barcode Identifier shall be assigned by USPS to encode the
  2175. * presort identification that is currently printed in human readable form on the optional endorsement line (OEL)
  2176. * as well as for future USPS use. This shall be two digits, with the second digit in the range of 0–4. The
  2177. * allowable encoding ranges shall be 00–04, 10–14, 20–24, 30–34, 40–44, 50–54, 60–64, 70–74, 80–84, and
  2178. * 90–94.</li><li>The Service Type Identifier shall be assigned by USPS for any combination of services requested
  2179. * on the mailpiece. The allowable encoding range shall be 000http://it2.php.net/manual/en/function.dechex.php–999.
  2180. * Each 3-digit value shall correspond to a particular mail class with a particular combination of service(s). Each
  2181. * service program, such as OneCode Confirm and OneCode ACS, shall provide the list of Service Type Identifier
  2182. * values.</li><li>The Mailer or Customer Identifier shall be assigned by USPS as a unique, 6 or 9 digit number
  2183. * that identifies a business entity. The allowable encoding range for the 6 digit Mailer ID shall be 000000-
  2184. * 899999, while the allowable encoding range for the 9 digit Mailer ID shall be 900000000-999999999.</li><li>The
  2185. * Serial or Sequence Number shall be assigned by the mailer for uniquely identifying and tracking mailpieces. The
  2186. * allowable encoding range shall be 000000000–999999999 when used with a 6 digit Mailer ID and 000000-999999 when
  2187. * used with a 9 digit Mailer ID. e. The Delivery Point ZIP Code shall be assigned by the mailer for routing the
  2188. * mailpiece. This shall replace POSTNET for routing the mailpiece to its final delivery point. The length may be
  2189. * 0, 5, 9, or 11 digits. The allowable encoding ranges shall be no ZIP Code, 00000–99999, 000000000–999999999,
  2190. * and 00000000000–99999999999.</li></ul>
  2191. *
  2192. * @param $code (string) code to print, separate the ZIP (routing code) from the rest using a minus char '-'
  2193. * (BarcodeID_ServiceTypeID_MailerID_SerialNumber-RoutingCode)
  2194. * @return array barcode representation.
  2195. * @protected
  2196. */
  2197. protected function barcode_imb($code)
  2198. {
  2199. $asc_chr = array(
  2200. 4,
  2201. 0,
  2202. 2,
  2203. 6,
  2204. 3,
  2205. 5,
  2206. 1,
  2207. 9,
  2208. 8,
  2209. 7,
  2210. 1,
  2211. 2,
  2212. 0,
  2213. 6,
  2214. 4,
  2215. 8,
  2216. 2,
  2217. 9,
  2218. 5,
  2219. 3,
  2220. 0,
  2221. 1,
  2222. 3,
  2223. 7,
  2224. 4,
  2225. 6,
  2226. 8,
  2227. 9,
  2228. 2,
  2229. 0,
  2230. 5,
  2231. 1,
  2232. 9,
  2233. 4,
  2234. 3,
  2235. 8,
  2236. 6,
  2237. 7,
  2238. 1,
  2239. 2,
  2240. 4,
  2241. 3,
  2242. 9,
  2243. 5,
  2244. 7,
  2245. 8,
  2246. 3,
  2247. 0,
  2248. 2,
  2249. 1,
  2250. 4,
  2251. 0,
  2252. 9,
  2253. 1,
  2254. 7,
  2255. 0,
  2256. 2,
  2257. 4,
  2258. 6,
  2259. 3,
  2260. 7,
  2261. 1,
  2262. 9,
  2263. 5,
  2264. 8
  2265. );
  2266. $dsc_chr = array(
  2267. 7,
  2268. 1,
  2269. 9,
  2270. 5,
  2271. 8,
  2272. 0,
  2273. 2,
  2274. 4,
  2275. 6,
  2276. 3,
  2277. 5,
  2278. 8,
  2279. 9,
  2280. 7,
  2281. 3,
  2282. 0,
  2283. 6,
  2284. 1,
  2285. 7,
  2286. 4,
  2287. 6,
  2288. 8,
  2289. 9,
  2290. 2,
  2291. 5,
  2292. 1,
  2293. 7,
  2294. 5,
  2295. 4,
  2296. 3,
  2297. 8,
  2298. 7,
  2299. 6,
  2300. 0,
  2301. 2,
  2302. 5,
  2303. 4,
  2304. 9,
  2305. 3,
  2306. 0,
  2307. 1,
  2308. 6,
  2309. 8,
  2310. 2,
  2311. 0,
  2312. 4,
  2313. 5,
  2314. 9,
  2315. 6,
  2316. 7,
  2317. 5,
  2318. 2,
  2319. 6,
  2320. 3,
  2321. 8,
  2322. 5,
  2323. 1,
  2324. 9,
  2325. 8,
  2326. 7,
  2327. 4,
  2328. 0,
  2329. 2,
  2330. 6,
  2331. 3
  2332. );
  2333. $asc_pos = array(
  2334. 3,
  2335. 0,
  2336. 8,
  2337. 11,
  2338. 1,
  2339. 12,
  2340. 8,
  2341. 11,
  2342. 10,
  2343. 6,
  2344. 4,
  2345. 12,
  2346. 2,
  2347. 7,
  2348. 9,
  2349. 6,
  2350. 7,
  2351. 9,
  2352. 2,
  2353. 8,
  2354. 4,
  2355. 0,
  2356. 12,
  2357. 7,
  2358. 10,
  2359. 9,
  2360. 0,
  2361. 7,
  2362. 10,
  2363. 5,
  2364. 7,
  2365. 9,
  2366. 6,
  2367. 8,
  2368. 2,
  2369. 12,
  2370. 1,
  2371. 4,
  2372. 2,
  2373. 0,
  2374. 1,
  2375. 5,
  2376. 4,
  2377. 6,
  2378. 12,
  2379. 1,
  2380. 0,
  2381. 9,
  2382. 4,
  2383. 7,
  2384. 5,
  2385. 10,
  2386. 2,
  2387. 6,
  2388. 9,
  2389. 11,
  2390. 2,
  2391. 12,
  2392. 6,
  2393. 7,
  2394. 5,
  2395. 11,
  2396. 0,
  2397. 3,
  2398. 2
  2399. );
  2400. $dsc_pos = array(
  2401. 2,
  2402. 10,
  2403. 12,
  2404. 5,
  2405. 9,
  2406. 1,
  2407. 5,
  2408. 4,
  2409. 3,
  2410. 9,
  2411. 11,
  2412. 5,
  2413. 10,
  2414. 1,
  2415. 6,
  2416. 3,
  2417. 4,
  2418. 1,
  2419. 10,
  2420. 0,
  2421. 2,
  2422. 11,
  2423. 8,
  2424. 6,
  2425. 1,
  2426. 12,
  2427. 3,
  2428. 8,
  2429. 6,
  2430. 4,
  2431. 4,
  2432. 11,
  2433. 0,
  2434. 6,
  2435. 1,
  2436. 9,
  2437. 11,
  2438. 5,
  2439. 3,
  2440. 7,
  2441. 3,
  2442. 10,
  2443. 7,
  2444. 11,
  2445. 8,
  2446. 2,
  2447. 10,
  2448. 3,
  2449. 5,
  2450. 8,
  2451. 0,
  2452. 3,
  2453. 12,
  2454. 11,
  2455. 8,
  2456. 4,
  2457. 5,
  2458. 1,
  2459. 3,
  2460. 0,
  2461. 7,
  2462. 12,
  2463. 9,
  2464. 8,
  2465. 10
  2466. );
  2467. $code_arr = explode('-', $code);
  2468. $tracking_number = $code_arr[0];
  2469. if (isset($code_arr[1])) {
  2470. $routing_code = $code_arr[1];
  2471. } else {
  2472. $routing_code = '';
  2473. }
  2474. // Conversion of Routing Code
  2475. switch (strlen($routing_code)) {
  2476. case 0: {
  2477. $binary_code = 0;
  2478. break;
  2479. }
  2480. case 5: {
  2481. $binary_code = bcadd($routing_code, '1');
  2482. break;
  2483. }
  2484. case 9: {
  2485. $binary_code = bcadd($routing_code, '100001');
  2486. break;
  2487. }
  2488. case 11: {
  2489. $binary_code = bcadd($routing_code, '1000100001');
  2490. break;
  2491. }
  2492. default: {
  2493. throw new BarcodeException('Routing code unknown');
  2494. break;
  2495. }
  2496. }
  2497. $binary_code = bcmul($binary_code, 10);
  2498. $binary_code = bcadd($binary_code, $tracking_number[0]);
  2499. $binary_code = bcmul($binary_code, 5);
  2500. $binary_code = bcadd($binary_code, $tracking_number[1]);
  2501. $binary_code .= substr($tracking_number, 2, 18);
  2502. // convert to hexadecimal
  2503. $binary_code = $this->dec_to_hex($binary_code);
  2504. // pad to get 13 bytes
  2505. $binary_code = str_pad($binary_code, 26, '0', STR_PAD_LEFT);
  2506. // convert string to array of bytes
  2507. $binary_code_arr = chunk_split($binary_code, 2, "\r");
  2508. $binary_code_arr = substr($binary_code_arr, 0, -1);
  2509. $binary_code_arr = explode("\r", $binary_code_arr);
  2510. // calculate frame check sequence
  2511. $fcs = $this->imb_crc11fcs($binary_code_arr);
  2512. // exclude first 2 bits from first byte
  2513. $first_byte = sprintf('%2s', dechex((hexdec($binary_code_arr[0]) << 2) >> 2));
  2514. $binary_code_102bit = $first_byte . substr($binary_code, 2);
  2515. // convert binary data to codewords
  2516. $codewords = array();
  2517. $data = $this->hex_to_dec($binary_code_102bit);
  2518. $codewords[0] = bcmod($data, 636) * 2;
  2519. $data = bcdiv($data, 636);
  2520. for ($i = 1; $i < 9; ++$i) {
  2521. $codewords[$i] = bcmod($data, 1365);
  2522. $data = bcdiv($data, 1365);
  2523. }
  2524. $codewords[9] = $data;
  2525. if (($fcs >> 10) == 1) {
  2526. $codewords[9] += 659;
  2527. }
  2528. // generate lookup tables
  2529. $table2of13 = $this->imb_tables(2, 78);
  2530. $table5of13 = $this->imb_tables(5, 1287);
  2531. // convert codewords to characters
  2532. $characters = array();
  2533. $bitmask = 512;
  2534. foreach ($codewords as $k => $val) {
  2535. if ($val <= 1286) {
  2536. $chrcode = $table5of13[$val];
  2537. } else {
  2538. $chrcode = $table2of13[($val - 1287)];
  2539. }
  2540. if (($fcs & $bitmask) > 0) {
  2541. // bitwise invert
  2542. $chrcode = ((~$chrcode) & 8191);
  2543. }
  2544. $characters[] = $chrcode;
  2545. $bitmask /= 2;
  2546. }
  2547. $characters = array_reverse($characters);
  2548. // build bars
  2549. $k = 0;
  2550. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
  2551. for ($i = 0; $i < 65; ++$i) {
  2552. $asc = (($characters[$asc_chr[$i]] & pow(2, $asc_pos[$i])) > 0);
  2553. $dsc = (($characters[$dsc_chr[$i]] & pow(2, $dsc_pos[$i])) > 0);
  2554. if ($asc AND $dsc) {
  2555. // full bar (F)
  2556. $p = 0;
  2557. $h = 3;
  2558. } elseif ($asc) {
  2559. // ascender (A)
  2560. $p = 0;
  2561. $h = 2;
  2562. } elseif ($dsc) {
  2563. // descender (D)
  2564. $p = 1;
  2565. $h = 2;
  2566. } else {
  2567. // tracker (T)
  2568. $p = 1;
  2569. $h = 1;
  2570. }
  2571. $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
  2572. $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  2573. $bararray['maxw'] += 2;
  2574. }
  2575. unset($bararray['bcode'][($k - 1)]);
  2576. --$bararray['maxw'];
  2577. return $bararray;
  2578. }
  2579. /**
  2580. * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
  2581. *
  2582. * @param $code (string) pre-formatted IMB barcode (65 chars "FADT")
  2583. * @return array barcode representation.
  2584. * @protected
  2585. */
  2586. protected function barcode_imb_pre($code)
  2587. {
  2588. if ( ! preg_match('/^[fadtFADT]{65}$/', $code) == 1) {
  2589. throw new InvalidFormatException();
  2590. }
  2591. $characters = str_split(strtolower($code), 1);
  2592. // build bars
  2593. $k = 0;
  2594. $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
  2595. for ($i = 0; $i < 65; ++$i) {
  2596. switch ($characters[$i]) {
  2597. case 'f': {
  2598. // full bar
  2599. $p = 0;
  2600. $h = 3;
  2601. break;
  2602. }
  2603. case 'a': {
  2604. // ascender
  2605. $p = 0;
  2606. $h = 2;
  2607. break;
  2608. }
  2609. case 'd': {
  2610. // descender
  2611. $p = 1;
  2612. $h = 2;
  2613. break;
  2614. }
  2615. case 't': {
  2616. // tracker (short)
  2617. $p = 1;
  2618. $h = 1;
  2619. break;
  2620. }
  2621. }
  2622. $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
  2623. $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
  2624. $bararray['maxw'] += 2;
  2625. }
  2626. unset($bararray['bcode'][($k - 1)]);
  2627. --$bararray['maxw'];
  2628. return $bararray;
  2629. }
  2630. /**
  2631. * Convert large integer number to hexadecimal representation.
  2632. * (requires PHP bcmath extension)
  2633. *
  2634. * @param $number (string) number to convert specified as a string
  2635. * @return string hexadecimal representation
  2636. */
  2637. protected function dec_to_hex($number)
  2638. {
  2639. if ($number == 0) {
  2640. return '00';
  2641. }
  2642. $hex = [];
  2643. while ($number > 0) {
  2644. array_push($hex, strtoupper(dechex(bcmod($number, '16'))));
  2645. $number = bcdiv($number, '16', 0);
  2646. }
  2647. $hex = array_reverse($hex);
  2648. return implode($hex);
  2649. }
  2650. /**
  2651. * Convert large hexadecimal number to decimal representation (string).
  2652. * (requires PHP bcmath extension)
  2653. *
  2654. * @param $hex (string) hexadecimal number to convert specified as a string
  2655. * @return string hexadecimal representation
  2656. */
  2657. protected function hex_to_dec($hex)
  2658. {
  2659. $dec = 0;
  2660. $bitval = 1;
  2661. $len = strlen($hex);
  2662. for ($pos = ($len - 1); $pos >= 0; --$pos) {
  2663. $dec = bcadd($dec, bcmul(hexdec($hex{$pos}), $bitval));
  2664. $bitval = bcmul($bitval, 16);
  2665. }
  2666. return $dec;
  2667. }
  2668. /**
  2669. * Intelligent Mail Barcode calculation of Frame Check Sequence
  2670. *
  2671. * @param $code_arr (string) array of hexadecimal values (13 bytes holding 102 bits right justified).
  2672. * @return int 11 bit Frame Check Sequence as integer (decimal base)
  2673. * @protected
  2674. */
  2675. protected function imb_crc11fcs($code_arr)
  2676. {
  2677. $genpoly = 0x0F35; // generator polynomial
  2678. $fcs = 0x07FF; // Frame Check Sequence
  2679. // do most significant byte skipping the 2 most significant bits
  2680. $data = hexdec($code_arr[0]) << 5;
  2681. for ($bit = 2; $bit < 8; ++$bit) {
  2682. if (($fcs ^ $data) & 0x400) {
  2683. $fcs = ($fcs << 1) ^ $genpoly;
  2684. } else {
  2685. $fcs = ($fcs << 1);
  2686. }
  2687. $fcs &= 0x7FF;
  2688. $data <<= 1;
  2689. }
  2690. // do rest of bytes
  2691. for ($byte = 1; $byte < 13; ++$byte) {
  2692. $data = hexdec($code_arr[$byte]) << 3;
  2693. for ($bit = 0; $bit < 8; ++$bit) {
  2694. if (($fcs ^ $data) & 0x400) {
  2695. $fcs = ($fcs << 1) ^ $genpoly;
  2696. } else {
  2697. $fcs = ($fcs << 1);
  2698. }
  2699. $fcs &= 0x7FF;
  2700. $data <<= 1;
  2701. }
  2702. }
  2703. return $fcs;
  2704. }
  2705. /**
  2706. * Reverse unsigned short value
  2707. *
  2708. * @param $num (int) value to reversr
  2709. * @return int reversed value
  2710. * @protected
  2711. */
  2712. protected function imb_reverse_us($num)
  2713. {
  2714. $rev = 0;
  2715. for ($i = 0; $i < 16; ++$i) {
  2716. $rev <<= 1;
  2717. $rev |= ($num & 1);
  2718. $num >>= 1;
  2719. }
  2720. return $rev;
  2721. }
  2722. /**
  2723. * generate Nof13 tables used for Intelligent Mail Barcode
  2724. *
  2725. * @param $n (int) is the type of table: 2 for 2of13 table, 5 for 5of13table
  2726. * @param $size (int) size of table (78 for n=2 and 1287 for n=5)
  2727. * @return array requested table
  2728. * @protected
  2729. */
  2730. protected function imb_tables($n, $size)
  2731. {
  2732. $table = array();
  2733. $lli = 0; // LUT lower index
  2734. $lui = $size - 1; // LUT upper index
  2735. for ($count = 0; $count < 8192; ++$count) {
  2736. $bit_count = 0;
  2737. for ($bit_index = 0; $bit_index < 13; ++$bit_index) {
  2738. $bit_count += intval(($count & (1 << $bit_index)) != 0);
  2739. }
  2740. // if we don't have the right number of bits on, go on to the next value
  2741. if ($bit_count == $n) {
  2742. $reverse = ($this->imb_reverse_us($count) >> 3);
  2743. // if the reverse is less than count, we have already visited this pair before
  2744. if ($reverse >= $count) {
  2745. // If count is symmetric, place it at the first free slot from the end of the list.
  2746. // 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
  2747. if ($reverse == $count) {
  2748. $table[$lui] = $count;
  2749. --$lui;
  2750. } else {
  2751. $table[$lli] = $count;
  2752. ++$lli;
  2753. $table[$lli] = $reverse;
  2754. ++$lli;
  2755. }
  2756. }
  2757. }
  2758. }
  2759. return $table;
  2760. }
  2761. protected function convertBarcodeArrayToNewStyle($oldBarcodeArray)
  2762. {
  2763. $newBarcodeArray = [];
  2764. $newBarcodeArray['code'] = $oldBarcodeArray['code'];
  2765. $newBarcodeArray['maxWidth'] = $oldBarcodeArray['maxw'];
  2766. $newBarcodeArray['maxHeight'] = $oldBarcodeArray['maxh'];
  2767. $newBarcodeArray['bars'] = [];
  2768. foreach ($oldBarcodeArray['bcode'] as $oldbar) {
  2769. $newBar = [];
  2770. $newBar['width'] = $oldbar['w'];
  2771. $newBar['height'] = $oldbar['h'];
  2772. $newBar['positionVertical'] = $oldbar['p'];
  2773. $newBar['drawBar'] = $oldbar['t'];
  2774. $newBar['drawSpacing'] = ! $oldbar['t'];
  2775. $newBarcodeArray['bars'][] = $newBar;
  2776. }
  2777. return $newBarcodeArray;
  2778. }
  2779. }