root/trunk/thirdparty/cl-pdf/bar-codes.lisp

Revision 2636, 11.0 kB (checked in by hans, 10 months ago)

add cl-pdf for pixel->pdf converter

Line 
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")
Note: See TracBrowser for help on using the browser.