Wrzasq.pl

Generator kodów kreskowych

Tuesday, 06 January 2009, 04:11

Jak zawsze samodzielny

Przy okazji tworzenia nowej oddsłony pewnego serwisu zetknąłem się z kolejnym nowym problemem - tworzeniem kodów kreskowych. Trzeba było napisać aplikację generującą takie kody. Do tej pory nigdy nie zajmowałem się ich tworzeniem, a jedynie obsługą od strony bazy danych - tworzylem programy ktore zarządzały wcześniej zczytanymi kodami. Tym razem musiałem je generować. Dla mnie była to nowość, ale jak zawsze w takich sytuacjach wyszukałem potrzebne mi informacje i specyfikacje i bazując na nich napisałem kod odpowiedzialny za te czarne paski. Niestety zauważyłem, że jest to kolejna problematyka, która nie jest raczej nigdzie poruszana, bo jedyne materiały jakie znalazłem to oficjalne standardyzacje kodów, a pozostałe znaleziska to zazwyczaj pytania na forach bez odpowiedzi. Dlatego postanowiłem się podzielić kolejną pigułką wiedzy i omówić tworzenie takich kodów z poziomu PHP przy użyciu biblioteki GD.

Ponieważ jedyne do czego generowałem te kody to przesyłki pocztowe, to przedstawione przezemnie przykłady będą się opierać właśnie o kody generowane dla przesyłek Pocztą Polską - są to kody GS1-128, a jeszcze dokładniej ograniczę się do SSCC (format kodów do oznaczania przesyłek właśnie), ale nie powinno być problemu z dostosowaniem tych fragmentów do innych potrzeb.

Jak w ogóle ma to działać

Na początek krótko o kodach. Jest wiele formatów kodów kreskowych, jednak najczęściej używane są kody GS1-128 - są bardzo uniwersalne i proste w obsłudze. W tym formacie informacje zapisane są za pomocą pionowych pasków (nie - nie wszystkie kody paskowe tak wyglądają, istnieją na przykład zapisywane w postaci macieży). Jednostką szerokości jest tutaj moduł (czyli najcieńsza kreska) - każdy pasek ma szerokość wyrażoną w modułach (szerokość jednego modułu w różnych zastosowaniach jest różna i określana dla każdego przypadku, tak jak określa to PP). To z czego składa się dany znak zapisany w kodzie paskowym znajdziecie w specyfikacji kodów. Oprócz znaków informacji istnieją dodatkowe znako zapisywane w kodzie - przede wszystkim znaki startu i stopu: są to specjalne kombinacje modułów które zawsze oznaczają początek i kodu; znaki funkcyjne - informacje specjalne, najczęściej o znaczeniu zależnym od przeznaczenia danego kodu; oraz znaki kontrolne - sumy kontrolne pomagające sprawdzić, czy kod został poprawnie odczytany.

W kodach GS1-128 mamy trzy zestawy znaków. Przy czym zestaw A i B służą do kodowania całego alfabetu ASCII, a ostatni C zawiera tylko cyfry dzięki czemu jest on krótszy gdyż jeden znak zapisuje dwie cyfry. Z wspomnianego właśnie powodu w kodach SSCC stosuje się zestaw C.

Dokładnie struktury kodów nie chcę tutaj opisywać, ponieważ ten artykuł ma za zadanie ułatwić życie przede wszystkim ludzią tworzącym aplikacje do sklepów internetowych generujące nadruki na paczki. Dlatego pominę opis poszczególnych pól danych. Nasz konkretny kod do używania w przesyłkach pocztowych składa się z następujących elementów:

znak rozpoczęcia
Jest to znak rozpoczynający kod oraz ustawiający kodowanie. W przypadku naszych kodów dla Poczty Polskiej zawsze będzie to znak Start C.
znaku funkcyjnego
Oznacza, że następny znak ma znaczenie specjalne. Konkretnie będzie on identyfikatorem usługi. U nas znak funkcyjny to zawsze FNC1.
identyfikatora usługi
Dla usług wysyłkowych jest to zawsze 00 (ponieważ używamy zestawu znaków C obie cyfry zapiszemy jednym znakiem).
17 znakowy kod
Jest to właściwy identyfikator, który mamy zapisać, czyli dane przechowywane przez kod.
suma kontrolna danych
Suma kontrolna numeru przesyłki wyliczana według właściwego algorytmu.
suma kontrolna kodu
Suma kontrolna Modulo 103 całego kodu. Nie należy jej mylić z poprzedzającą ją sumą kontrolną danych. Dodatkowa różnica jest taka, że suma kontrolna danych jest dopisywana do liczbowej reprezentacji danych drukowanej na papierze, natomiast suma kontrolna kodu nie jest widoczna w cyfrach, które widzi człowiek.
znak stopu
Czyli koniec całego kodu.

Warto pamiętać, że kody te są międzynarodowym standardem więc wiedza ta może się nie raz przydać.

Sumy kontrolne

Samo generowanie kodu kreskowego nie powinno już być takie trudne, ponieważ sprowadza się praktycznie do układania obok siebie kolejno pasków białych i czarnych. Należy jedynie odczytywać kolejne symbole z tablicy znaków. Jedyne zagadkowe wartości to sumy kontrolne. Pierwsza z nich to suma kontrolna samych danych, czyli 17-to znakowego kodu przesyłki. Tutaj miałem na początku dziwne rozbieżne informacje, ponieważ w oficjalnych specyfikacjach pisane było, iż sumę tę liczy się metodą Modulo 110 podczas gdy wytyczne Poczty Polskiej mówią o odejmowaniu wyniku od najbliższej wzwyż wielokrotności 10-tki. Tak, czy siak wzór jest nieco bardziej złożony, a ostatecznie przedstawię wersję Poczty Polskiej, bo przecież to u nich te kody mają działać, w tym momencie oficjalne standardyzacje mnie nie interesują :P. Otóż sumę tą liczy się w następujący sposób:

  1. Mnoży się co drugą cyfrę (począwszy od pierwszej) przez 3, pozostałe przez 1.
  2. Sumuje się iloczyny.
  3. Oblicza się resztę z dzielenia przez 10.
  4. Odejmuje się otrzymaną resztę od 10.
  5. Jeśli resztą było 0 to wynikiem sumy kontrolnej też będzie 0, a nie 10 jak by wydzhodziło z poprzedniego punktu.

Drugą sumę kontrolną - całego pakietu (chyba można tak nazwać kod kreskowy?) liczy się z kolei w taki oto sposób:

  1. Przypisuje się pierwszym dwóm znakom (Start C i FNC1) wagi 1, a każdym następnym wagę o jeden większą niż poprzednia (czyli wagi utworzą ciąg 1, 1, 2, 3, 4, …).
  2. Mnoży się znaki przez ich wagi.
  3. Sumuje się iloczyny.
  4. Oblicza się resztę z dzielenia przez 103.

Generowanie kodów

W czasie generowania kodów należy wziąc po uwagę rozdzielczość z jaką bedziemy drukować potem obraz. W specyfikacji GS1-128 wiele razy wspomina się o czułości urządzeń i wrażliwości nadruków, zalecana rozdzielczość wydruku to minimum 200dpi (trzeba też pamiętać o wysokiej jakości i dużym kontraście).

/**
 * Klasa do generowania właściwego obrazu z kodem paskowym.
 * 
 * @see http://www.php.net/manual/en/book.image.php
 * @link https://wrzasq.pl/
 * @author Wrzasq <wrzasq@gmail.com>
 * @copyright 2008 - 2009 (C) by Wrzasq
 * @license http://www.opensource.org/licenses/bsd-license.php
 */
class BarCodeImage
{
/**
 * Zasób obrazka na którym zapisujemy kod kreskowy.
 * 
 * @var resource
 */
    protected $image;
/**
 * Rozdzielczość nadruku - to wazne, bo wszelkie dane dotyczące kodów sa podawane w milimetrach.
 * 
 * @var int
 */
    protected $dpi;
/**
 * Szerokość pojedynczego modułu.
 * 
 * @var float
 */
    protected $unit;
/**
 * Wysokość kodu.
 * 
 * @var float
 */
    protected $size;
/**
 * Aktualna pozycja w kodzie.
 * 
 * @var int
 */
    protected $position = 0;
/**
 * Margines wolny od nadruków.
 * 
 * @var float
 */
    protected $margin;

/**
 * Biały kolor.
 * 
 * @var int
 */
    protected $white;
/**
 * Czarny kolor.
 * 
 * @var int
 */
    protected $black;

/**
 * Przelicza milimetry na piksele według ustawionej rozdzielczości. Może się okazać, że konieczne będzie zaokrąglanie wyników do liczb całkowitych, jednak dopuki poniższa wersja nie będzie powodować zapisów lepiej tego nie robić, gdyż może to zniekształcić proporcje szerokości.
 * 
 * @param int $metric Wielkość w milimetrach.
 * @return float Przeliczenie na piksele.
 */
    protected function mm2px($metric)
    {
        return $metric * $this->dpi / 25.4;
//        return round($metric * $this->dpi / 25.4);
    }

/**
 * Tworzy nowy obrazek kodu paskowego.
 * 
 * @param int $width Szerokość przestrzeni na nadruk (w milimetrach).
 * @param int $height Wysokość przestrzeni na nadruk (w milimetrach).
 * @param int $pdi Docelowa rozdzielczość nadruku (domyślnie 200 DPI).
 */
    public function __construct($width, $height, $dpi = 200)
    {
        $this->dpi = $dpi;
        $this->image = imagecreatetruecolor( $this->mm2px($width), $this->mm2px($height) );
        $this->white = imagecolorallocate($this->image, 255, 255, 255);
        $this->black = imagecolorallocate($this->image, 0, 0, 0);
        imagefill($this->image, 0, 0, $this->white);
    }

/**
 * Ustawia szerokośc modułu.
 * 
 * @param float $width Szerokość (w milimetrach).
 */
    public function setUnit($width)
    {
        $this->unit = $this->mm2px($width);
    }

/**
 * Ustawia wysokość nadruku pasków.
 * 
 * @param float $height Wysokość pasków (w milimetrach).
 */
    public function setSize($height)
    {
        $this->size = $this->mm2px($height);
    }

/**
 * Ustawia margines - wymagana jest wolna przestrzeń między kodem paskowym a jakimkolwiek nadrukiem.
 * 
 * @param float $margin Zakres marginesu (w milimetrach).
 */
    public function setMargin($margin)
    {
        $this->margin = $this->mm2px($margin);
    }

/**
 * Dodaje pasek o danej szerokości i wartości.
 * 
 * @param bool $value Wartośc danego segmentu - FALSE dla 0 (biały segment), TRUE dla 1 (czarny).
 * @param int $width Szerokość segmentu (wyrażona w jednostkach - modułach).
 */
    public function addBar($value, $width)
    {
        $color = $value ? $this->black : $this->white;
        $start = $this->position * $this->unit + $this->margin;

        imagefilledrectangle($this->image, $start, $this->margin, $start + $width * $this->unit, $this->margin + $this->size, $color);

        $this->position += $width;
    }

/**
 * Automatycznie dodaje całą serię segmentów.
 * 
 * @param array $widths Wartości kolejnych segmentów.
 * @param bool $value Wartość pierwszego segmentu. Domyślnie 1, ale może się okazać, że konieczna jest zmiana.
 */
    public function addBars(array $widths, $value = true)
    {
        foreach($widths as $width)
        {
            $this->addBar($value, $width);
            $value = !$value;
        }
    }

/**
 * Zwraca wygenerowany obraz nadruku.
 * 
 * @return resource Obraz z kodem paskowym.
 */
    public function getImage()
    {
        return $this->image;
    }
}

/**
 * Podstawowa klasa tłumaczenia znaków. Nie implementowałem znaków nie używanych w naszym przypadku. Oddzielam zestaw C do klasy potomnej aby można było łatwo zaimplementować zestawy A i B. Zwracane Wartości są tablicami zawierającymi na przemian szerokość segmentu czarnego i białego z czego zawsze pierwszy jest czarny.
 * 
 * @link https://wrzasq.pl/
 * @author Wrzasq <wrzasq@gmail.com>
 * @copyright 2008 - 2009 (C) by Wrzasq
 * @license http://www.opensource.org/licenses/bsd-license.php
 */
abstract class BarCodeCharset
{
/**
 * Zwraca znak startu C.
 * 
 * @return array Zapis znaku Start C.
 */
    final public function getStartC()
    {
        return array(2, 1, 1, 2, 3, 2);
    }

/**
 * Zwraca znak funkcyjnego FNC1.
 * 
 * @return array Zapis znaku FNC1.
 */
    final public function getFNC1()
    {
        return array(4, 1, 1, 1, 3, 1);
    }

/**
 * Zwraca znak stop.
 * 
 * @return array Zapis znaku Stop.
 */
    final public function getStop()
    {
        return array(2, 3, 3, 1, 1, 1, 2);
    }

/**
 * Tłumaczy znak w aktualnym zestawie znaków na format paskowy.
 * 
 * @param string $character Znak do przetłumaczenia.
 * @return array Lista pasków.
 */
    abstract public function translate($character);
}

/**
 * Tłumaczenie zestawu C.
 * 
 * @link https://wrzasq.pl/
 * @author Wrzasq <wrzasq@gmail.com>
 * @copyright 2008 - 2009 (C) by Wrzasq
 * @license http://www.opensource.org/licenses/bsd-license.php
 */
class BarCodeCharsetC extends BarCodeCharset
{
/**
 * Tłumaczy znak w zestawie C. Należy pamiętać, że tłumaczone są dwie cyfry jednocześnie.
 * 
 * @param string $character Dwie cyfry. W szczególnym przypadku może być to wartość większa niż 100 na przykład jeśli suma kontrolna pakietu okaże się taką liczbą.
 * @return array Lista pasków.
 */
    public function translate($character)
    {
        static $charset = array(0 => array(2, 1, 2, 2, 2, 2), 1 => array(2, 2, 2, 1, 2, 2), 2 => array(2, 2, 2, 2, 2, 1), 3 => array(1, 2, 1, 2, 2, 3), 4 => array(1, 2, 1, 3, 2, 2), 5 => array(1, 3, 1, 2, 2, 2), 6 => array(1, 2, 2, 2, 1, 3), 7 => array(1, 2, 2, 3, 1, 2), 8 => array(1, 3, 2, 2, 1, 2), 9 => array(2, 2, 1, 2, 1, 3), 10 => array(2, 2, 1, 3, 1, 2), 11 => array(2, 3, 1, 2, 1, 2), 12 => array(1, 1, 2, 2, 3, 2), 13 => array(1, 2, 2, 1, 3, 2), 14 => array(1, 2, 2, 2, 3, 1), 15 => array(1, 1, 3, 2, 2, 2), 16 => array(1, 2, 3, 1, 2, 2), 17 => array(1, 2, 3, 2, 2, 1), 18 => array(2, 2, 3, 2, 1, 1), 19 => array(2, 2, 1, 1, 3, 2), 20 => array(2, 2, 1, 2, 3, 1), 21 => array(2, 1, 3, 2, 1, 2), 22 => array(2, 2, 3, 1, 1, 2), 23 => array(3, 1, 2, 1, 3, 1), 24 => array(3, 1, 1, 2, 2, 2), 25 => array(3, 2, 1, 1, 2, 2), 26 => array(3, 2, 1, 2, 2, 1), 27 => array(3, 1, 2, 2, 1, 2), 28 => array(3, 2, 2, 1, 1, 2), 29 => array(3, 2, 2, 2, 1, 1), 30 => array(2, 1, 2, 1, 2, 3), 31 => array(2, 1, 2, 3, 2, 1), 32 => array(2, 3, 2, 1, 2, 1), 33 => array(1, 1, 1, 3, 2, 3), 34 => array(1, 3, 1, 1, 2, 3), 35 => array(1, 3, 1, 3, 2, 1), 36 => array(1, 1, 2, 3, 1, 3), 37 => array(1, 3, 2, 1, 1, 3), 38 => array(1, 3, 2, 3, 1, 1), 39 => array(2, 1, 1, 3, 1, 3), 40 => array(2, 3, 1, 1, 1, 3), 41 => array(2, 3, 1, 3, 1, 1), 42 => array(1, 1, 2, 1, 3, 3), 43 => array(1, 1, 2, 3, 3, 1), 44 => array(1, 3, 2, 1, 3, 1), 45 => array(1, 1, 3, 1, 2, 3), 46 => array(1, 1, 3, 3, 2, 1), 47 => array(1, 3, 3, 1, 2, 1), 48 => array(3, 1, 3, 1, 2, 1), 49 => array(2, 1, 1, 3, 3, 1), 50 => array(2, 3, 1, 1, 3, 1), 51 => array(2, 1, 3, 1, 1, 3), 52 => array(2, 1, 3, 3, 1, 1), 53 => array(2, 1, 3, 1, 3, 1), 54 => array(3, 1, 1, 1, 2, 3), 55 => array(3, 1, 1, 3, 2, 1), 56 => array(3, 3, 1, 1, 2, 1), 57 => array(3, 1, 2, 1, 1, 3), 58 => array(3, 1, 2, 3, 1, 1), 59 => array(3, 3, 2, 1, 1, 1), 60 => array(3, 1, 4, 1, 1, 1), 61 => array(2, 2, 1, 4, 1, 1), 62 => array(4, 3, 1, 1, 1, 1), 63 => array(1, 1, 1, 2, 2, 4), 64 => array(1, 1, 1, 4, 2, 2), 65 => array(1, 2, 1, 1, 2, 4), 66 => array(1, 2, 1, 4, 2, 1), 67 => array(1, 4, 1, 1, 2, 2), 68 => array(1, 4, 1, 2, 2, 1), 69 => array(1, 1, 2, 2, 1, 4), 70 => array(1, 1, 2, 4, 1, 2), 71 => array(1, 2, 2, 1, 1, 4), 72 => array(1, 2, 2, 4, 1, 1), 73 => array(1, 4, 2, 1, 1, 2), 74 => array(1, 4, 2, 2, 1, 1), 75 => array(2, 4, 1, 2, 1, 1), 76 => array(2, 2, 1, 1, 1, 4), 77 => array(4, 1, 3, 1, 1, 1), 78 => array(2, 4, 1, 1, 1, 2), 79 => array(1, 3, 4, 1, 1, 1), 80 => array(1, 1, 1, 2, 4, 2), 81 => array(1, 2, 1, 1, 4, 2), 82 => array(1, 2, 1, 2, 4, 1), 83 => array(1, 1, 4, 2, 1, 2), 84 => array(1, 2, 4, 1, 1, 2), 85 => array(1, 2, 4, 2, 1, 1), 86 => array(4, 1, 1, 2, 1, 2), 87 => array(4, 2, 1, 1, 1, 2), 88 => array(4, 2, 1, 2, 1, 1), 89 => array(2, 1, 2, 1, 4, 1), 90 => array(2, 1, 4, 1, 2, 1), 91 => array(4, 1, 2, 1, 2, 1), 92 => array(1, 1, 1, 1, 4, 3), 93 => array(1, 1, 1, 3, 4, 1), 94 => array(1, 3, 1, 1, 4, 1), 95 => array(1, 1, 4, 1, 1, 3), 96 => array(1, 1, 4, 3, 1, 1), 97 => array(4, 1, 1, 1, 1, 3), 98 => array(4, 1, 1, 3, 1, 1), 99 => array(1, 1, 3, 1, 4, 1), 100 => array(1, 1, 4, 1, 3, 1), 101 => array(3, 1, 1, 1, 4, 1), 102 => array(4, 1, 1, 1, 3, 1) );

        return $charset[ (int) $character];
    }
}

/**
 * Przechowuje zawartośc kodu kreskowego. Umożliwia zarówno ustawienie całości kodu jak i dodawanie kolejnych znaków.
 * 
 * @link https://wrzasq.pl/
 * @author Wrzasq <wrzasq@gmail.com>
 * @copyright 2008 - 2009 (C) by Wrzasq
 * @license http://www.opensource.org/licenses/bsd-license.php
 */
abstract class BarCode
{
/**
 * Zawartośc kodu.
 * 
 * @var string
 */
    protected $code = '';
/**
 * Szerokość obrazka.
 * 
 * @var float
 */
    protected $width;
/**
 * Wysokość obrazka.
 * 
 * @var float
 */
    protected $height;
/**
 * Szerokość modułu.
 * 
 * @var float
 */
    protected $unit;
/**
 * Wysokość kodu kreskowego.
 * 
 * @var float
 */
    protected $size;
/**
 * Rozdzielczość nadruku.
 * 
 * @var int
 */
    protected $dpi = 200;
/**
 * Rozmiar marginesu.
 * 
 * @var float
 */
    protected $margin;

/**
 * Tworzy obraz z kodem paskowym.
 * 
 * @return resource Wygenerowany obrazek.
 */
    abstract public function generateImage();

/**
 * Ustawia szerokośc całego obrazu.
 * 
 * @param float $width Szerokość obszaru (w milimetrach).
 */
    public function setWidth($width)
    {
        $this->width = $width;
    }

/**
 * Ustawia wysokość całego obrazu.
 * 
 * @param float $height Wysokość obszaru (w milimetrach).
 */
    public function setHeight($height)
    {
        $this->height = $height;
    }

/**
 * Ustawia szerokośc modułu.
 * 
 * @param float $width Szerokość (w milimetrach).
 */
    public function setUnit($width)
    {
        $this->unit = $width;
    }

/**
 * Ustawia wysokość nadruku pasków.
 * 
 * @param float $height Wysokość pasków (w milimetrach).
 */
    public function setSize($height)
    {
        $this->size = $height;
    }

/**
 * Ustawia rozdzielczość nadruku.
 * 
 * @param int $dpi Rozdzielczośc w DPI (ilość pikseli na cal).
 */
    public function setDPI($dpi)
    {
        $this->dpi = $dpi;
    }

/**
 * Ustawia margines - wymagana jest wolna przestrzeń między kodem paskowym a jakimkolwiek nadrukiem.
 * 
 * @param float $margin Zakres marginesu (w milimetrach).
 */
    public function setMargin($margin)
    {
        $this->margin = $margin;
    }

/**
 * Ustawia zawartość kodu na daną wartość.
 * 
 * @param string $code Wartośc kodu paskowego.
 */
    public function setCode($code)
    {
        $this->code = $code;
    }

/**
 * Dopisuje dany fragment do kodu.
 * 
 * @param string $code Wartość do dopisania.
 */
    public function appendPart($code)
    {
        $this->code .= $code;
    }
}

/**
 * Generuje obraz kodu GS1-128.
 * 
 * @link https://wrzasq.pl/
 * @author Wrzasq <wrzasq@gmail.com>
 * @copyright 2008 - 2009 (C) by Wrzasq
 * @license http://www.opensource.org/licenses/bsd-license.php
 */
class BarCodeGS1128 extends BarCode
{
/**
 * Tworzy obraz z kodem paskowym. Automatycznie oblicza sumę kontrolną pakietu.
 * 
 * @return resource Wygenerowany obrazek.
 */
    public function generateImage()
    {
        // właściwy obraz
        $image = new BarCodeImage($this->width, $this->height, $this->dpi);
        $image->setUnit($this->unit);
        $image->setSize($this->size);
        $image->setMargin($this->margin);

        // zestaw znaków C
        $charset = new BarCodeCharsetC();

        // dodaje znaki Start C i FNC1
        $image->addBars( $charset->getStartC() );
        $image->addBars( $charset->getFNC1() );

        // początkowe wartości do obliczania sumy kontrolnej - ponieważ znaki Start C i FNC1 są stałe
        $sum = 207;
        $weight = 2;

        $code = $this->code;

        // odcinamy po dwie cyfry z kodu
        while( strlen($code) > 0)
        {
            // dodajemy kolejny iloraz do sumy kontrolnej oraz zapisujemy segmenty na obrazku
            $part = substr($code, 0, 2);
            $sum += $part * $weight++;
            $image->addBars( $charset->translate($part) );
            $code = substr($code, 2);
        }

        // dodajemy znak sumy kontrolnej pakietu i znak Stop
        $image->addBars( $charset->translate($sum % 103) );
        $image->addBars( $charset->getStop() );

        return $image->getImage();
    }
}

/**
 * Uściśla klasę kodów GS1-128 dla kodów SSCC (wysyłkowych).
 * 
 * @link https://wrzasq.pl/
 * @author Wrzasq <wrzasq@gmail.com>
 * @copyright 2008 - 2009 (C) by Wrzasq
 * @license http://www.opensource.org/licenses/bsd-license.php
 */
class BarCodeGS1128SSCC extends BarCodeGS1128
{
/**
 * Automatycznie dodaje do generowanego obrazka identyfikator aplikacji SSCC, oraz sumę kontrolną kodu.
 * 
 * @return resource Wygenerowany obrazek.
 */
    public function generateImage()
    {
        // oblicza sumę kontrolną
        $digit = 10 - (($this->code[0] * 3 + $this->code[1] * 1 + $this->code[2] * 3 + $this->code[3] * 1 + $this->code[4] * 3 + $this->code[5] * 1 + $this->code[6] * 3 + $this->code[7] * 1 + $this->code[8] * 3 + $this->code[9] * 1 + $this->code[10] * 3 + $this->code[11] * 1 + $this->code[12] * 3 + $this->code[13] * 1 + $this->code[14] * 3 + $this->code[15] * 1 + $this->code[16] * 3) % 10);

        // suma kontrolna jest jedną cyfrą
        if($digit == 10)
        {
            $digit = 0;
        }

        // dopisuje sygnaturę SSCC i sumę kontrolną
        $this->code = '00' . $this->code . $digit;

        return parent::generateImage();
    }
}

Oto przykład zastosowania:

// dane dotyczące rozmiarów według wytycznych Poczty Polskiej dla wysyłek pobraniowych
$barCode = new BarCodeGS1128SSCC();
$barCode->setWidth(76);
$barCode->setHeight(25);
$barCode->setUnit(0.254);
$barCode->setSize(15);
$barCode->setMargin(5);
$barCode->setCode('12345678910111213');
imagepng( $barCode->generateImage(), 'barcode.png');

A oto wynik:

Przykładowy kod kreskowy

Obrazki należy drukować w jaknajlepszej jakości gdyż nadruk czarny powinien dokładnie zapełniać białą powierzchnię papieru, aby czytniki kodów poprawnie odczytały wartości.

Alternatywne wyjście

Natomiast już po wykonaniu całości naszła mnie pewna myśl, ale już nie chciało mi się nawet eksperymentować ("if something works, then don't fix it" ;)), ale może komuś innemu się będzie chciało - co gdyby generować takie paski przy pomocy styli CSS? Dzięki zastosowaniu @media "print" można by być może uzyskać lepszą (dokładniejszą) interpretacje operując na jednostkach takich jak te podawane przez Pocztę Polską. Jeśli ktoś będzie próbował, to jestem ciekawy rezultatów :).

Tags: , , , , , ,