| 1 |
;;; cl-pdf copyright 2002-2003 Marc Battyani see license.txt for the details |
|---|
| 2 |
;;; You can reach me at marc.battyani@fractalconcept.com or marc@battyani.net |
|---|
| 3 |
;;; The homepage of cl-pdf is here: http://www.fractalconcept.com/asp/html/cl-pdf.html |
|---|
| 4 |
|
|---|
| 5 |
;; code 128 barcode from Lars Rustemeier |
|---|
| 6 |
|
|---|
| 7 |
(in-package #:pdf) |
|---|
| 8 |
|
|---|
| 9 |
;; |
|---|
| 10 |
;; Utils |
|---|
| 11 |
;; |
|---|
| 12 |
|
|---|
| 13 |
(defun group (string n) |
|---|
| 14 |
(when (zerop n) (error "zero length")) |
|---|
| 15 |
(loop with length = (length string) |
|---|
| 16 |
for i from 0 below length by n |
|---|
| 17 |
collect (subseq string i (min (+ i n) length)))) |
|---|
| 18 |
|
|---|
| 19 |
;; |
|---|
| 20 |
;; Logic Layer |
|---|
| 21 |
;; |
|---|
| 22 |
|
|---|
| 23 |
;; Format: |
|---|
| 24 |
;; [Value] [Code A] [Code B] [Code C] [Pattern (B S B S B S)] |
|---|
| 25 |
(defparameter *table* |
|---|
| 26 |
'((0 #\space #\space "00" 2 -1 2 -2 2 -2) |
|---|
| 27 |
(1 #\! #\! "01" 2 -2 2 -1 2 -2) |
|---|
| 28 |
(2 #\" #\" "02" 2 -2 2 -2 2 -1) |
|---|
| 29 |
(3 #\# #\# "03" 1 -2 1 -2 2 -3) |
|---|
| 30 |
(4 #\$ #\$ "04" 1 -2 1 -3 2 -2) |
|---|
| 31 |
(5 #\% #\% "05" 1 -3 1 -2 2 -2) |
|---|
| 32 |
(6 #\& #\& "06" 1 -2 2 -2 1 -3) |
|---|
| 33 |
(7 #\' #\' "07" 1 -2 2 -3 1 -2) |
|---|
| 34 |
(8 #\( #\( "08" 1 -3 2 -2 1 -2) |
|---|
| 35 |
(9 #\) #\) "09" 2 -2 1 -2 1 -3) |
|---|
| 36 |
(10 #\* #\* "10" 2 -2 1 -3 1 -2) |
|---|
| 37 |
(11 #\+ #\+ "11" 2 -3 1 -2 1 -2) |
|---|
| 38 |
(12 #\, #\, "12" 1 -1 2 -2 3 -2) |
|---|
| 39 |
(13 #\- #\- "13" 1 -2 2 -1 3 -2) |
|---|
| 40 |
(14 #\. #\. "14" 1 -2 2 -2 3 -1) |
|---|
| 41 |
(15 #\/ #\/ "15" 1 -1 3 -2 2 -2) |
|---|
| 42 |
(16 #\0 #\0 "16" 1 -2 3 -1 2 -2) |
|---|
| 43 |
(17 #\1 #\1 "17" 1 -2 3 -2 2 -1) |
|---|
| 44 |
(18 #\2 #\2 "18" 2 -2 3 -2 1 -1) |
|---|
| 45 |
(19 #\3 #\3 "19" 2 -2 1 -1 3 -2) |
|---|
| 46 |
(20 #\4 #\4 "20" 2 -2 1 -2 3 -1) |
|---|
| 47 |
(21 #\5 #\5 "21" 2 -1 3 -2 1 -2) |
|---|
| 48 |
(22 #\6 #\6 "22" 2 -2 3 -1 1 -2) |
|---|
| 49 |
(23 #\7 #\7 "23" 3 -1 2 -1 3 -1) |
|---|
| 50 |
(24 #\8 #\8 "24" 3 -1 1 -2 2 -2) |
|---|
| 51 |
(25 #\9 #\9 "25" 3 -2 1 -1 2 -2) |
|---|
| 52 |
(26 #\: #\: "26" 3 -2 1 -2 2 -1) |
|---|
| 53 |
(27 #\; #\; "27" 3 -1 2 -2 1 -2) |
|---|
| 54 |
(28 #\< #\< "28" 3 -2 2 -1 1 -2) |
|---|
| 55 |
(29 #\= #\= "29" 3 -2 2 -2 1 -1) |
|---|
| 56 |
(30 #\> #\> "30" 2 -1 2 -1 2 -3) |
|---|
| 57 |
(31 #\? #\? "31" 2 -1 2 -3 2 -1) |
|---|
| 58 |
(32 #\@ #\@ "32" 2 -3 2 -1 2 -1) |
|---|
| 59 |
(33 #\A #\A "33" 1 -1 1 -3 2 -3) |
|---|
| 60 |
(34 #\B #\B "34" 1 -3 1 -1 2 -3) |
|---|
| 61 |
(35 #\C #\C "35" 1 -3 1 -3 2 -1) |
|---|
| 62 |
(36 #\D #\D "36" 1 -1 2 -3 1 -3) |
|---|
| 63 |
(37 #\E #\E "37" 1 -3 2 -1 1 -3) |
|---|
| 64 |
(38 #\F #\F "38" 1 -3 2 -3 1 -1) |
|---|
| 65 |
(39 #\G #\G "39" 2 -1 1 -3 1 -3) |
|---|
| 66 |
(40 #\H #\H "40" 2 -3 1 -1 1 -3) |
|---|
| 67 |
(41 #\I #\I "41" 2 -3 1 -3 1 -1) |
|---|
| 68 |
(42 #\J #\J "42" 1 -1 2 -1 3 -3) |
|---|
| 69 |
(43 #\K #\K "43" 1 -1 2 -3 3 -1) |
|---|
| 70 |
(44 #\L #\L "44" 1 -3 2 -1 3 -1) |
|---|
| 71 |
(45 #\M #\M "45" 1 -1 3 -1 2 -3) |
|---|
| 72 |
(46 #\N #\N "46" 1 -1 3 -3 2 -1) |
|---|
| 73 |
(47 #\O #\O "47" 1 -3 3 -1 2 -1) |
|---|
| 74 |
(48 #\P #\P "48" 3 -1 3 -1 2 -1) |
|---|
| 75 |
(49 #\Q #\Q "49" 2 -1 1 -3 3 -1) |
|---|
| 76 |
(50 #\R #\R "50" 2 -3 1 -1 3 -1) |
|---|
| 77 |
(51 #\S #\S "51" 2 -1 3 -1 1 -3) |
|---|
| 78 |
(52 #\T #\T "52" 2 -1 3 -3 1 -1) |
|---|
| 79 |
(53 #\U #\U "53" 2 -1 3 -1 3 -1) |
|---|
| 80 |
(54 #\V #\V "54" 3 -1 1 -1 2 -3) |
|---|
| 81 |
(55 #\W #\W "55" 3 -1 1 -3 2 -1) |
|---|
| 82 |
(56 #\X #\X "56" 3 -3 1 -1 2 -1) |
|---|
| 83 |
(57 #\Y #\Y "57" 3 -1 2 -1 1 -3) |
|---|
| 84 |
(58 #\Z #\Z "58" 3 -1 2 -3 1 -1) |
|---|
| 85 |
(59 #\[ #\[ "59" 3 -3 2 -1 1 -1) |
|---|
| 86 |
(60 #\\ #\\ "60" 3 -1 4 -1 1 -1) |
|---|
| 87 |
(61 #\] #\] "61" 2 -2 1 -4 1 -1) |
|---|
| 88 |
(62 #\^ #\^ "62" 4 -3 1 -1 1 -1) |
|---|
| 89 |
(63 #\_ #\_ "63" 1 -1 1 -2 2 -4) |
|---|
| 90 |
(64 :NUL #\' "64" 1 -1 1 -4 2 -2) |
|---|
| 91 |
(65 :SOH #\a "65" 1 -2 1 -1 2 -4) |
|---|
| 92 |
(66 :STX #\b "66" 1 -2 1 -4 2 -1) |
|---|
| 93 |
(67 :ETX #\c "67" 1 -4 1 -1 2 -2) |
|---|
| 94 |
(68 :EOT #\d "68" 1 -4 1 -2 2 -1) |
|---|
| 95 |
(69 :ENQ #\e "69" 1 -1 2 -2 1 -4) |
|---|
| 96 |
(70 :ACK #\f "70" 1 -1 2 -4 1 -2) |
|---|
| 97 |
(71 :BEL #\g "71" 1 -2 2 -1 1 -4) |
|---|
| 98 |
(72 :BS #\h "72" 1 -2 2 -4 1 -1) |
|---|
| 99 |
(73 :HT #\i "73" 1 -4 2 -1 1 -2) |
|---|
| 100 |
(74 :LF #\j "74" 1 -4 2 -2 1 -1) |
|---|
| 101 |
(75 :VT #\k "75" 2 -4 1 -2 1 -1) |
|---|
| 102 |
(76 :FF #\l "76" 2 -2 1 -1 1 -4) |
|---|
| 103 |
(77 :CR #\m "77" 4 -1 3 -1 1 -1) |
|---|
| 104 |
(78 :SO #\n "78" 2 -4 1 -1 1 -2) |
|---|
| 105 |
(79 :SI #\o "79" 1 -3 4 -1 1 -1) |
|---|
| 106 |
(80 :DLE #\p "80" 1 -1 1 -2 4 -2) |
|---|
| 107 |
(81 :DC1 #\q "81" 1 -2 1 -1 4 -2) |
|---|
| 108 |
(82 :DC2 #\r "82" 1 -2 1 -2 4 -1) |
|---|
| 109 |
(83 :DC3 #\s "83" 1 -1 4 -2 1 -2) |
|---|
| 110 |
(84 :DC4 #\t "84" 1 -2 4 -1 1 -2) |
|---|
| 111 |
(85 :NAK #\u "85" 1 -2 4 -2 1 -1) |
|---|
| 112 |
(86 :SYN #\v "86" 4 -1 1 -2 1 -2) |
|---|
| 113 |
(87 :ETB #\w "87" 4 -2 1 -1 1 -2) |
|---|
| 114 |
(88 :CAN #\x "88" 4 -2 1 -2 1 -1) |
|---|
| 115 |
(89 :EM #\y "89" 2 -1 2 -1 4 -1) |
|---|
| 116 |
(90 :SUB #\z "90" 2 -1 4 -1 2 -1) |
|---|
| 117 |
(91 :ESC #\{ "91" 4 -1 2 -1 2 -1) |
|---|
| 118 |
(92 :FS #\| "92" 1 -1 1 -1 4 -3) |
|---|
| 119 |
(93 :GS #\} "93" 1 -1 1 -3 4 -1) |
|---|
| 120 |
(94 :RS #\~ "94" 1 -3 1 -1 4 -1) |
|---|
| 121 |
(95 :US :DEL "95" 1 -1 4 -1 1 -3) |
|---|
| 122 |
(96 :FNC-3 :FNC-3 "96" 1 -1 4 -3 1 -1) |
|---|
| 123 |
(97 :FNC-2 :FNC-2 "97" 4 -1 1 -1 1 -3) |
|---|
| 124 |
(98 :SHIFT :SHIFT "98" 4 -1 1 -3 1 -1) |
|---|
| 125 |
(99 :CODE-C :CODE-C "99" 1 -1 3 -1 4 -1) |
|---|
| 126 |
(100 :CODE-B :FNC-4 :CODE-B 1 -1 4 -1 3 -1) |
|---|
| 127 |
(101 :FNC-4 :CODE-A :CODE-A 3 -1 1 -1 4 -1) |
|---|
| 128 |
(102 :FNC-1 :FNC-1 :FNC-1 4 -1 1 -1 3 -1) |
|---|
| 129 |
(103 :START-A :START-A :START-A 2 -1 1 -4 1 -2) |
|---|
| 130 |
(104 :START-B :START-B :START-B 2 -1 1 -2 1 -4) |
|---|
| 131 |
(105 :START-C :START-C :START-C 2 -1 1 -2 3 -2) |
|---|
| 132 |
(106 :STOP :STOP :STOP 2 -3 3 -1 1 -1 2))) |
|---|
| 133 |
|
|---|
| 134 |
(defconstant +magic-modulo-number+ 103) |
|---|
| 135 |
|
|---|
| 136 |
(defun build-char-ht (lst hpred key-selector val-selector) |
|---|
| 137 |
(let ((ht (make-hash-table :test hpred))) |
|---|
| 138 |
(dolist (e lst) |
|---|
| 139 |
(setf (gethash (funcall key-selector e) ht) (funcall val-selector e))) |
|---|
| 140 |
ht)) |
|---|
| 141 |
|
|---|
| 142 |
(defparameter *table-h* (build-char-ht *table* #'eql #'first #'cdr)) |
|---|
| 143 |
(defparameter *table-a* (build-char-ht *table* #'eql #'second #'(lambda (e) (cons (first e) (cddddr e))))) |
|---|
| 144 |
(defparameter *table-b* (build-char-ht *table* #'eql #'third #'(lambda (e) (cons (first e) (cddddr e))))) |
|---|
| 145 |
(defparameter *table-c* (build-char-ht *table* #'equal #'fourth #'(lambda (e) (cons (first e) (cddddr e))))) |
|---|
| 146 |
|
|---|
| 147 |
|
|---|
| 148 |
;; |
|---|
| 149 |
;; Calulate checksum |
|---|
| 150 |
;; |
|---|
| 151 |
|
|---|
| 152 |
(defun code128-checksum (chars) |
|---|
| 153 |
(mod (do ((vals (cdr chars) (cdr vals)) |
|---|
| 154 |
(factor 1 (+ factor 1)) |
|---|
| 155 |
(chksum (first chars) (+ chksum (* (car vals) factor)))) |
|---|
| 156 |
((null vals) |
|---|
| 157 |
chksum)) |
|---|
| 158 |
+magic-modulo-number+)) |
|---|
| 159 |
|
|---|
| 160 |
;; |
|---|
| 161 |
;; Generic |
|---|
| 162 |
;; |
|---|
| 163 |
|
|---|
| 164 |
(defun code128-n-raw (string start s-table getter) |
|---|
| 165 |
(let ((chars-and-bars (cons (gethash start s-table) |
|---|
| 166 |
(map 'list #'(lambda (s) (gethash s s-table)) string)))) |
|---|
| 167 |
(let* ((chars (mapcar #'car chars-and-bars)) |
|---|
| 168 |
(bars (mapcar #'cdr chars-and-bars)) |
|---|
| 169 |
(chksum (code128-checksum chars)) |
|---|
| 170 |
(chk-char-bar (cdr (gethash (funcall getter (gethash chksum *table-h*)) s-table))) |
|---|
| 171 |
(stop (cdr (gethash :STOP s-table)))) |
|---|
| 172 |
(append bars (list chk-char-bar stop))))) |
|---|
| 173 |
|
|---|
| 174 |
;; |
|---|
| 175 |
;; Chars and control codes |
|---|
| 176 |
;; |
|---|
| 177 |
(defun code128-a (text) |
|---|
| 178 |
(code128-n-raw text :START-A *table-a* #'first)) |
|---|
| 179 |
|
|---|
| 180 |
;; |
|---|
| 181 |
;; Full printable ascii |
|---|
| 182 |
;; |
|---|
| 183 |
(defun code128-b (text) |
|---|
| 184 |
(code128-n-raw text :START-B *table-b* #'second)) |
|---|
| 185 |
|
|---|
| 186 |
;; |
|---|
| 187 |
;; Compact digits |
|---|
| 188 |
;; |
|---|
| 189 |
(defun code128-c (text) |
|---|
| 190 |
(code128-n-raw (group text 2) :START-C *table-c* #'third)) |
|---|
| 191 |
|
|---|
| 192 |
;; |
|---|
| 193 |
;; Calculate width of barcode in units |
|---|
| 194 |
;; |
|---|
| 195 |
|
|---|
| 196 |
(defun unit-width (lst) |
|---|
| 197 |
;;(reduce + 0 (map (lambda (e) (reduce + 0 e)) (map second lst)))) |
|---|
| 198 |
(reduce #'+ (mapcar #'(lambda (e) (reduce #'+ (mapcar #'abs e))) lst))) |
|---|
| 199 |
|
|---|
| 200 |
;; |
|---|
| 201 |
;; Drawing (Presentation Layer) |
|---|
| 202 |
;; |
|---|
| 203 |
|
|---|
| 204 |
(defun draw-bar-segment (line-height line-width black) |
|---|
| 205 |
(with-saved-state |
|---|
| 206 |
(when (plusp black) |
|---|
| 207 |
(rectangle 0 0 (* black line-width) (- line-height)) |
|---|
| 208 |
(fill-path))) |
|---|
| 209 |
(translate (abs (* black line-width)) 0)) |
|---|
| 210 |
|
|---|
| 211 |
(defun draw-bars (bars line-height line-width &optional (start-stop-factor 0)) |
|---|
| 212 |
(let ((l (length bars))) |
|---|
| 213 |
(do* ((bars bars (cdr bars)) |
|---|
| 214 |
(bar (car bars) (car bars)) |
|---|
| 215 |
(i 0 (1+ i))) |
|---|
| 216 |
((null bars) |
|---|
| 217 |
nil) |
|---|
| 218 |
(dolist (segment bar) |
|---|
| 219 |
(let ((line-height (if (or (= i 0) (= i (- l 1))) |
|---|
| 220 |
(* line-height (+ 1 start-stop-factor)) |
|---|
| 221 |
line-height))) |
|---|
| 222 |
(draw-bar-segment line-height line-width segment)))))) |
|---|
| 223 |
|
|---|
| 224 |
(defun draw-chars (string line-height line-width font font-size segs-per-char) |
|---|
| 225 |
(with-saved-state |
|---|
| 226 |
(translate (* 11 line-width) (- (+ line-height font-size))) |
|---|
| 227 |
(set-font font font-size) |
|---|
| 228 |
(loop for char across string do |
|---|
| 229 |
(with-saved-state |
|---|
| 230 |
(in-text-mode |
|---|
| 231 |
(show-char char))) |
|---|
| 232 |
(translate (* segs-per-char line-width) 0)))) |
|---|
| 233 |
|
|---|
| 234 |
;; |
|---|
| 235 |
;; Autoselect's mode based on content |
|---|
| 236 |
;; |
|---|
| 237 |
;; One could spend a lot of time on making really smart (Switching |
|---|
| 238 |
;; between alphabets on the fly etc). I fear that for more advanced |
|---|
| 239 |
;; uses it is better to let the user specify the token list |
|---|
| 240 |
;; manually. Or build utils on top of this file's functionality. There |
|---|
| 241 |
;; simply are too many border cases for this to make me feel right |
|---|
| 242 |
;; about a totally automatic approach. |
|---|
| 243 |
|
|---|
| 244 |
(defun draw-bar-code128 (string x y &key (font (get-font)) (font-size 5) (start-stop-factor 0.3) (height 100) (width 400) (show-string t) (segs-per-char 11 segs-per-char-p)) |
|---|
| 245 |
(let ((dispatch #'code128-b)) |
|---|
| 246 |
(when (and (evenp (length string))(every #'digit-char-p string)) |
|---|
| 247 |
(unless segs-per-char-p |
|---|
| 248 |
(setf segs-per-char 5.5)) |
|---|
| 249 |
(setf dispatch #'code128-c)) |
|---|
| 250 |
(with-saved-state |
|---|
| 251 |
(translate x y) |
|---|
| 252 |
(set-line-width 0) |
|---|
| 253 |
(let* ((bars (funcall dispatch string)) |
|---|
| 254 |
(unit-w (unit-width bars)) |
|---|
| 255 |
(line-width (/ width unit-w)) |
|---|
| 256 |
(line-height height)) |
|---|
| 257 |
(with-saved-state |
|---|
| 258 |
(draw-bars bars line-height line-width start-stop-factor)) |
|---|
| 259 |
(when show-string |
|---|
| 260 |
(with-saved-state |
|---|
| 261 |
(draw-chars string line-height line-width font font-size segs-per-char))))))) |
|---|
| 262 |
|
|---|
| 263 |
;; |
|---|
| 264 |
;; Pres test code |
|---|
| 265 |
;; |
|---|
| 266 |
|
|---|
| 267 |
#+nil |
|---|
| 268 |
(defun tester (str &optional (to-file "/tmp/foobar.pdf")) |
|---|
| 269 |
(pdf:with-document () |
|---|
| 270 |
(pdf:with-page () |
|---|
| 271 |
(pdf:with-outline-level ((format nil "BarCodes Sample") (pdf:register-page-reference)) |
|---|
| 272 |
(pdf:with-saved-state |
|---|
| 273 |
(pdf:translate -150 -400) |
|---|
| 274 |
(pdf:rotate 90) |
|---|
| 275 |
(pdf:translate 500 -300) |
|---|
| 276 |
(pdf:rotate 5) |
|---|
| 277 |
(draw-bar-code128 str 0 0 :height 300 :width 600 :start-stop-factor 0.2 :font-size 40 :show-string t)))) |
|---|
| 278 |
(pdf:write-document to-file))) |
|---|
| 279 |
|
|---|
| 280 |
|
|---|
| 281 |
#+nil |
|---|
| 282 |
(tester "CL:PDF Marc B" #P"/home/largo/char-b.pdf") |
|---|
| 283 |
#+nil |
|---|
| 284 |
(tester "012345" #P"/home/largo/char-c.pdf") |
|---|