root/trunk/thirdparty/cl-pdf/png.lisp

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

add cl-pdf for pixel->pdf converter

Line 
1 ;;; cl-pdf copyright 2002-2005 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 (in-package #:pdf)
6
7 ;;; PNG Images code proposal by Dmitri Ivanov
8 ;;; Notes:
9 ;;;  The /ProcSet entry of page resource dictionary should be expanded to
10 ;;;     [/PDF /Text /ImageB /ImageC /ImageI]
11 ;;;  for compatibility with existing viewer applications.
12 ;;;  It is considered obsolete in PDF 1.4 (section 9.1)
13 ;;;
14 ;;;  Acrobat error "There was an error processing a page. A drawing error occurred"
15 ;;;  Reason: Acrobat 5 has a bug and is unable to display transparent monochrome images.
16 ;;;  Workaround: Remove transparency or save your image in 16 colors (4 bits per pixel)
17 ;;;  or more.
18
19
20 (declaim (ftype (function (stream) (unsigned-byte 16)) read-byte16)
21          (inline read-byte16))
22
23 (defun read-byte16 (stream)
24  ;;; Read a 2-byte integer
25   (the (unsigned-byte 16) (+ (ash (read-byte stream) 8) (read-byte stream))))
26
27 (declaim (ftype (function (stream) (unsigned-byte 32)) read-byte32))
28 (defun read-byte32 (stream)
29  ;;; Read a 4-byte integer
30   (the (unsigned-byte 32)
31         (+ (ash (read-byte stream) 24) (ash (read-byte stream) 16)
32            (ash (read-byte stream) 8) (read-byte stream))))
33
34 (defun read-base-string (stream byte-count)
35   (let ((result (make-string byte-count :element-type 'base-char)))
36     (dotimes (i byte-count result)
37       (declare (fixnum i))
38       (setf (schar result i) (code-char (read-byte stream))))))
39
40 (defclass png-image (bitmap-image)
41  ((bits-per-color :accessor bits-per-color :initarg :bits-per-color)
42   ;(color-space :accessor color-space :initarg :color-space)
43   (palette :accessor palette :initarg :palette)
44   (mask :accessor mask :initarg :mask)))
45
46 (defun read-png-file (pathname)
47   (with-open-file (stream pathname :direction :input :element-type '(unsigned-byte 8))
48    (flet ((skip-octets (octet-length)
49             (dotimes (i octet-length) (read-byte stream))
50             (values)))
51     (declare (ftype (function (fixnum) (values)) skip-octets))
52     ;; Check signature
53     (unless (and (= (read-byte stream) 137)
54                  (= (read-byte stream) #.(char-code #\P))
55                  (= (read-byte stream) #.(char-code #\N))
56                  (= (read-byte stream) #.(char-code #\G))
57                  (= (read-byte stream) 13)
58                  (= (read-byte stream) 10)
59                  (= (read-byte stream) 26)
60                  (= (read-byte stream) 10))
61       (error "Incorrect PNG file ~s - wrong signature." pathname))
62
63     ;; Read header
64     (skip-octets 4)
65     (when (string/= (read-base-string stream 4) "IHDR")
66       (error "Incorrect PNG file ~s - wrong header." pathname))
67     (let* ((width (read-byte32 stream))
68            (height (read-byte32 stream))
69            (bits-per-color (read-byte stream))
70            (cs (read-byte stream))
71            (color-space (case cs
72                           (0 "DeviceGray")
73                           (2 "DeviceRGB")
74                           (3 "Indexed")
75                           (otherwise (error "Alpha channel not supported ~s" pathname))))
76            palette mask data)
77       (when (> bits-per-color 8) (error "16-bit depth not supported ~s" pathname))
78       (when (/= (read-byte stream) 0) (error "Unknown compression method ~s" pathname))
79       (when (/= (read-byte stream) 0) (error "Unknown filter method ~s" pathname))
80       (when (/= (read-byte stream) 0) (error "Interlacing not supported ~s" pathname))
81       (skip-octets 4)
82
83       ;; Extract palette, transparency and data if any
84       (do ((octet-length (read-byte32 stream) (read-byte32 stream))
85            (marker (read-base-string stream 4) (read-base-string stream 4)))
86           ((= octet-length 0))
87         ;(declare (type (unsigned-byte 32) octet-length)
88         (cond ((string= marker "PLTE")
89                ;; Palette: octet-length should be a multiple of 3
90                (setq palette (make-array octet-length :element-type '(unsigned-byte 8)))
91                (read-sequence palette stream))
92               ((string= marker "tRNS")                          ; transparency info
93                (let ((trns (make-array octet-length :element-type '(unsigned-byte 8))))
94                  (read-sequence trns stream)
95                  (setq mask (case cs
96                               (0                                ; DeviceGray
97                                (list (aref trns 1)))
98                               (2                                ; DeviceRGB
99                                (list (aref trns 1) (aref trns 3) (aref trns 5)))
100                               (otherwise                        ; Indexed
101                                (let ((position (position 0 trns)))
102                                  (when position (list position))))))))
103               ((string= marker "IDAT")                          ; image data block
104                (let ((start 0))
105                  (if (null data)
106                      (setf data (make-array octet-length
107                                             :element-type '(unsigned-byte 8)
108                                             :adjustable t))
109                      (progn
110                        (setf start (first (array-dimensions data)))
111                        (adjust-array data (+ start octet-length))))
112                  (read-sequence data stream :start start)))
113               ((string= marker "IEND")
114                (return))
115               (t                ;"pHYs"
116                (skip-octets octet-length)))
117         (skip-octets 4))
118
119       (when (and (= cs 3) (null palette))                       ; Indexed
120         (error "Palette is missing in ~s" pathname))
121       (make-instance 'png-image :nb-components color-space
122                      :width width :height height  :data data
123                      :bits-per-color bits-per-color  :palette palette  :mask mask)))))
124
125 (defmethod make-image ((png png-image) &key &allow-other-keys)
126   ;; For color key masking, the Mask entry is an array of 2*N integers,
127   ;; [min1 max1 ... minN maxN], where N is the number of color components in the
128   ;; image's color space.
129   (let* ((nb-components (nb-components png))
130          (palette (palette png))
131          (lookup
132           (if palette ;(string= nb-components "Indexed")
133               (make-instance 'indirect-object :content  ; comperess is controlled by config
134                 (make-instance 'pdf-stream
135                                ;:dict-values `(("/Filter" . ,filter))
136                                :content palette
137                                :no-compression t))))
138          (mask (mask png)))
139     (make-instance 'pdf:image
140          :bits (data png)
141          :width (width png) :height (height png)
142          :color-space (if (string= nb-components "Indexed")
143                           (vector (pdf-name nb-components)
144                             "/DeviceRGB"                        ; base
145                             (1- (truncate (length palette) 3))  ; maximum valid index value
146                             lookup)
147                           (pdf-name nb-components))
148          :bits-per-color (bits-per-color png)
149          :decode (if (string= nb-components "DeviceCMYK") #(1 0 1 0 1 0 1 0))
150          :mask (when mask
151                  (apply #'vector (mapcan (lambda (i) (list i i)) mask)))
152          :filter "/FlateDecode"                         ; the only method we recognize
153          :decode-parms `(("/Predictor" . 15)
154                          ("/Colors" . ,(if (string= nb-components "DeviceRGB") 3 1))
155                          ("/BitsPerComponent" . ,(bits-per-color png))
156                          ("/Columns" . ,(width png)))
157          :no-compression t)))                           ; data bits already come compressed
158
159
Note: See TracBrowser for help on using the browser.