Sunday sore-throat edition
This commit is contained in:
		
							parent
							
								
									9f6397806f
								
							
						
					
					
						commit
						d292dc423f
					
				|  | @ -0,0 +1 @@ | |||
| Oleg Kiselyov, RT Happe (porter) | ||||
|  | @ -0,0 +1 @@ | |||
| Read entries from the (first) image directory of tiff files. | ||||
|  | @ -0,0 +1,325 @@ | |||
| * Tiff -- A port of Oleg Kiselyov's tiff code | ||||
| 
 | ||||
| Both the code and this documentation is derived from Oleg's files, cf. | ||||
| http://okmij.org/ftp/Scheme/binary-io.html#tiff | ||||
| 
 | ||||
| This distribution comes with two family-friendly tiff files in sunterlib/ | ||||
| scsh/tiff/. | ||||
| 
 | ||||
| ** Reading TIFF files | ||||
| 
 | ||||
| [ Oleg's announcement.  Main changes to the announced library: modularisation | ||||
|   and fake srfi-4 uniform vectors.  (The source files list the changes.)    ] | ||||
| 
 | ||||
| From posting-system@google.com Wed Oct  8 01:24:13 2003 | ||||
| Date: Tue, 7 Oct 2003 18:24:12 -0700 | ||||
| From: oleg@pobox.com (oleg@pobox.com) | ||||
| Newsgroups: comp.lang.scheme | ||||
| Subject: [ANN] Reading TIFF files | ||||
| Message-ID: <7eb8ac3e.0310071724.59bffe62@posting.google.com> | ||||
| Status: OR | ||||
| 
 | ||||
| This is to announce a Scheme library to read and analyze TIFF image | ||||
| files. We can use the library to obtain the dimensions of a TIFF | ||||
| image; the image name and description; the resolution and other | ||||
| meta-data. We can then load a pixel matrix or a colormap table. An | ||||
| accompanying tiff-prober program prints out the TIFF dictionary in a | ||||
| raw and polished formats. | ||||
| 
 | ||||
|      http://pobox.com/~oleg/ftp/Scheme/lib/tiff.scm | ||||
| 	dependencies: util.scm, char-encoding.scm, myenv.scm | ||||
|      http://pobox.com/~oleg/ftp/Scheme/tests/vtiff.scm | ||||
|         see also: gnu-head-sm.tif in the same directory | ||||
|      http://pobox.com/~oleg/ftp/Scheme/tiff-prober.scm | ||||
| 
 | ||||
| Features: | ||||
|    - The library handles TIFF files written in both endian formats | ||||
|    - A TIFF directory is treated somewhat as a SRFI-44 immutable | ||||
|      dictionary collection. Only the most basic SRFI-44 methods are | ||||
|      implemented, including the left fold iterator and the get method. | ||||
|    - An extensible tag dictionary translates between symbolic tag | ||||
|      names and numeric ones. Ditto for tag values. | ||||
|    - A tag dictionary for all TIFF 6 standard tags and values comes | ||||
|      with the library. A user can add the definitions of | ||||
|      his private tags. | ||||
|    - The library handles TIFF directory values of types: | ||||
|     (signed/unsigned) byte, short, long, rational; ASCII strings. | ||||
|    - A particular care is taken to properly handle values whose | ||||
|      total size is no more than 4 bytes. | ||||
|    - Array values (including the image matrix) are returned as | ||||
|      uniform vectors (SRFI-4) | ||||
|    - Values are read lazily. If you are only interested in the | ||||
|      dimensions of an image, the image matrix itself will not be loaded. | ||||
| 
 | ||||
| 
 | ||||
| Here's the result of running tiff-prober on the image of the GNU head | ||||
| (converted from JPEG to TIFF by xv). I hope I won't have any copyright | ||||
| problems with using and distributing that image. | ||||
| 
 | ||||
| Analyzing TIFF file tests/gnu-head-sm.tif... | ||||
| There are 15 entries in the TIFF directory | ||||
| they are | ||||
| TIFFTAG:IMAGEWIDTH, count 1, type short, value-offset 129 (0x81) | ||||
| TIFFTAG:IMAGELENGTH, count 1, type short, value-offset 122 (0x7A) | ||||
| TIFFTAG:BITSPERSAMPLE, count 1, type short, value-offset 8 (0x8) | ||||
| TIFFTAG:COMPRESSION, count 1, type short, value-offset 1 (0x1) | ||||
| TIFFTAG:PHOTOMETRIC, count 1, type short, value-offset 1 (0x1) | ||||
| TIFFTAG:IMAGEDESCRIPTION, count 29, type ascii str, value-offset 15932 (0x3E3C) | ||||
| TIFFTAG:STRIPOFFSETS, count 1, type long, value-offset 8 (0x8) | ||||
| TIFFTAG:ORIENTATION, count 1, type short, value-offset 1 (0x1) | ||||
| TIFFTAG:SAMPLESPERPIXEL, count 1, type short, value-offset 1 (0x1) | ||||
| TIFFTAG:ROWSPERSTRIP, count 1, type short, value-offset 122 (0x7A) | ||||
| TIFFTAG:STRIPBYTECOUNTS, count 1, type long, value-offset 15738 (0x3D7A) | ||||
| TIFFTAG:XRESOLUTION, count 1, type rational, value-offset 15962 (0x3E5A) | ||||
| TIFFTAG:YRESOLUTION, count 1, type rational, value-offset 15970 (0x3E62) | ||||
| TIFFTAG:PLANARCONFIG, count 1, type short, value-offset 1 (0x1) | ||||
| TIFFTAG:RESOLUTIONUNIT, count 1, type short, value-offset 2 (0x2) | ||||
| 
 | ||||
| image width:    129 | ||||
| image height:   122 | ||||
| image depth:    8 | ||||
| document name:  *NOT SPECIFIED* | ||||
| image description: | ||||
|   JPEG:gnu-head-sm.jpg 129x122 | ||||
| time stamp:     *NOT SPECIFIED* | ||||
| compression:    NONE | ||||
| 
 | ||||
| In particular, the dump of the tiff directory is produced by the | ||||
| following line of code | ||||
| 	  (print-tiff-directory tiff-dict (current-output-port)) | ||||
| To determine the width of the image, we do | ||||
| 	(tiff-directory-get tiff-dict 'TIFFTAG:IMAGEWIDTH not-spec) | ||||
| To determine the compression (as a symbol) we evaluate | ||||
| 	(tiff-directory-get-as-symbol tiff-dict 'TIFFTAG:COMPRESSION not-spec) | ||||
| 
 | ||||
| If an image directory contains private tags, they will be printed like | ||||
| the following: | ||||
| 
 | ||||
| private tag 33009, count 1, type signed long, value-offset 16500000 (0xFBC520) | ||||
| private tag 33010, count 1, type signed long, value-offset 4294467296 | ||||
|                                                                  (0xFFF85EE0) | ||||
| 
 | ||||
| A user may supply a dictionary of his private tags and enjoy | ||||
| the automatic translation from symbolic to numerical tag names. | ||||
| 
 | ||||
| The validation code vtiff.scm includes a function | ||||
| test-reading-pixel-matrix that demonstrates loading a pixel matrix of | ||||
| an image in an u8vector. The code can handle a single or multiple | ||||
| strips. | ||||
| 
 | ||||
| Portability: the library itself, tiff.scm, relies on the following | ||||
| extensions to R5RS: uniform vectors (SRFI-4); ascii->char function | ||||
| (which is on many systems just integer->char); trivial define-macro | ||||
| (which can be easily re-written into syntax-rules); let*-values | ||||
| (SRFI-11); records (SRFI-9). Actually, the code uses Gambit's native | ||||
| define-structures, which can be easily re-written into SRFI-9 | ||||
| records. The Scheme system should be able to represent the full range | ||||
| of 32-bit integers and should support rationals. | ||||
| 
 | ||||
| The most problematic extension is an endian port. The TIFF library | ||||
| assumes the existence of a data structure with the following | ||||
| operations | ||||
|    endian-port-set-bigendian!::   EPORT -> UNSPECIFIED | ||||
|    endian-port-set-littlendian!:: EPORT -> UNSPECIFIED | ||||
|    endian-port-read-int1:: EPORT -> UINTEGER (byte) | ||||
|    endian-port-read-int2:: EPORT -> UINTEGER | ||||
|    endian-port-read-int4:: EPORT -> UINTEGER | ||||
|    endian-port-setpos:: EPORT INTEGER -> UNSPECIFIED | ||||
| 
 | ||||
| The library uses solely these methods to access the input port. The | ||||
| endian port can be implemented in a R5RS Scheme system if we assume | ||||
| that the composition of char->integer and read-char yields a byte and | ||||
| if we read the whole file into a string or a u8vector | ||||
| (SRFI-4). Obviously, there are times when such a solution is not | ||||
| satisfactory. Therefore, tiff-prober and the validation code | ||||
| vtiff.scm rely on a Gambit-specific code. All major Scheme systems can | ||||
| implement endian ports in a similar vein -- alas, each in its own | ||||
| particular way. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ** Endian ports | ||||
| from structure endian. | ||||
| 
 | ||||
| We rely on an ENDIAN-PORT | ||||
| A port with the following operations | ||||
|   endian-port-set-bigendian!::   EPORT -> UNSPECIFIED | ||||
|   endian-port-set-littlendian!:: EPORT -> UNSPECIFIED | ||||
|   endian-port-read-int1:: EPORT -> UINTEGER (byte) | ||||
|   endian-port-read-int2:: EPORT -> UINTEGER | ||||
|   endian-port-read-int4:: EPORT -> UINTEGER | ||||
|   endian-port-setpos EPORT INTEGER -> UNSPECIFIED | ||||
| 
 | ||||
|   close-endian-port:: EPORT -> UNSPECIFIED | ||||
|   make-endian-port:: INPORT BOOLEAN -> EPORT | ||||
|     The boolean argument sets the endianness of the resulting endian-port, | ||||
|   boolean(most sigificant bit first).  After having wrapped the INPORT | ||||
|   in the EPORT, you should no longer manipulate the INPORT directly. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ** Tiff | ||||
| in structures TIFF and TIFFLET.  TIFFLET exports a survival package of | ||||
| bindings: | ||||
|   read-tiff-file, print-tiff-directory, tiff-directory-get(-as-symbol). | ||||
| Refined needs will require TIFF. | ||||
| 
 | ||||
| *** TIFF tags: codes and values | ||||
| 
 | ||||
| A tag dictionary, tagdict, record helps translate between | ||||
| tag-symbols and their numerical values. | ||||
| 
 | ||||
| tagdict-get-by-name TAGDICT TAG-NAME => INT | ||||
|   where TAG-NAME is a symbol. | ||||
| Translate a symbolic representation of a TIFF tag into a numeric | ||||
| representation. | ||||
| An error is raised if the lookup fails. | ||||
| 
 | ||||
| tagdict-get-by-num TAGDICT INT => TAG-NAME or #f | ||||
|   Translate from a numeric tag value to a symbolic representation, | ||||
| if it exists. Return #f otherwise. | ||||
| 
 | ||||
| tagdict-tagval-get-by-name TAGDICT TAG-NAME VAL-NAME => INT | ||||
|   where VAL-NAME is a symbol. | ||||
| Translate from the symbolic representation of a value associated | ||||
| with TAG-NAME in the TIFF directory, into the numeric representation. | ||||
| An error is raised if the lookup fails. | ||||
| 
 | ||||
| tagdict-tagval-get-by-num TAGDICT TAG-NAME INT => VAL-NAME or #f | ||||
|   Translate from a numeric value associated with TAG-NAME in the TIFF | ||||
| directory to a symbolic representation, if it exists. Return #f | ||||
| otherwise. | ||||
| 
 | ||||
| make-tagdict ((TAG-NAME INT (VAL-NAME . INT) ...) ...) | ||||
|   Build a tag dictionary | ||||
| 
 | ||||
| tagdict? TAGDICT -> BOOL | ||||
| 
 | ||||
| tagdict-add-all DEST-DICT SRC-DICT -> DEST-DICT | ||||
|   Join two dictionaries | ||||
| 
 | ||||
| tiff-standard-tagdict : TAGDICT | ||||
|   The variable tiff-standard-tagdict is initialized to the dictionary | ||||
| of standard TIFF tags (which you may look up in the first section above | ||||
| or in the source, tiff.scm). | ||||
| 
 | ||||
| Usage scenario: | ||||
|    (tagdict-get-by-name  tiff-standard-tagdict 'TIFFTAG:IMAGEWIDTH) => 256 | ||||
|    (tagdict-get-by-num   tiff-standard-tagdict 256) => 'TIFFTAG:IMAGEWIDTH | ||||
|    (tagdict-tagval-get-by-name tiff-standard-tagdict | ||||
|       'TIFFTAG:COMPRESSION 'LZW) => 5 | ||||
|    (tagdict-tagval-get-by-num  tiff-standard-tagdict | ||||
|       'TIFFTAG:COMPRESSION 5) => 'LZW | ||||
| 
 | ||||
|    (define extended-tagdict | ||||
|       (tagdict-add-all tiff-standard-tagdict | ||||
|          (make-tagdict | ||||
| 	   '((WAupper_left_lat 33004) | ||||
| 	     (WAhemisphere 33003 (North . 1) (South . 2)))))) | ||||
| 
 | ||||
| 
 | ||||
| *** TIFF directory entry | ||||
| 
 | ||||
| a descriptor of a TIFF "item", which can be image data, document description, | ||||
| time stamp, etc, depending on the tag. Thus an entry has the following | ||||
| structure: | ||||
|  unsigned short tag; | ||||
|  unsigned short type;          // data type: byte, short word, etc. | ||||
|  unsigned long  count;         // number of items; length in spec | ||||
|  unsigned long  val_offset;    // byte offset to field data | ||||
| 
 | ||||
| The values associated with each entry are disjoint and may appear anywhere | ||||
| in the file (so long as they are placed on a word boundary). | ||||
| 
 | ||||
| Note, If the value takes 4 bytes or less, then it is placed in the offset | ||||
| field to save space.  If the value takes less than 4 bytes, it is | ||||
| *left*-justified in the offset field. | ||||
| Note, that it's always *left* justified (stored in the lower bytes) | ||||
| no matter what the byte order (big- or little- endian) is! | ||||
| Here's the precise quote from the TIFF 6.0 specification: | ||||
| "To save time and space the Value Offset contains the Value instead of | ||||
| pointing to the Value if and only if the Value fits into 4 bytes. If | ||||
| the Value is shorter than 4 bytes, it is left-justified within the | ||||
| 4-byte Value Offset, i.e., stored in the lower- numbered | ||||
| bytes. Whether the Value fits within 4 bytes is determined by the Type | ||||
| and Count of the field." | ||||
| 
 | ||||
| tiff-dir-entry? TIFF-DIR-ENTRY => BOOLEAN | ||||
| tiff-dir-entry-tag TIFF-DIR-ENTRY => INTEGER | ||||
| tiff-dir-entry-type TIFF-DIR-ENTRY => INTEGER | ||||
| tiff-dir-entry-count TIFF-DIR-ENTRY => INTEGER | ||||
| tiff-dir-entry-val-offset TIFF-DIR-ENTRY => INTEGER | ||||
| tiff-dir-entry-value TIFF-DIR-ENTRY => VALUE | ||||
| 
 | ||||
| print-tiff-dir-entry TIFF-DIR-ENTRY TAGDICT OPORT -> UNSPECIFIED | ||||
|   Print the contents of TIFF-DIR-ENTRY onto the output port OPORT | ||||
| using TAGDICT to convert tag identifiers to symbolic names | ||||
| 
 | ||||
| 
 | ||||
| *** TIFF Image File Directory | ||||
| 
 | ||||
| TIFF directory is a collection of TIFF directory entries. The entries | ||||
| are sorted in an ascending order by tag. | ||||
| Note, a TIFF file can contain more than one directory (chained together). | ||||
| We handle only the first one. | ||||
| 
 | ||||
| We treat a TIFF image directory somewhat as an ordered, immutable, | ||||
| dictionary collection, see SRFI-44. | ||||
| 
 | ||||
| tiff-directory? VALUE => BOOLEAN | ||||
| tiff-directory-size TIFF-DIRECTORY => INTEGER | ||||
| tiff-directory-empty? TIFF-DIRECTORY => BOOLEAN | ||||
| 
 | ||||
| tiff-directory-get TIFF-DIRECTORY KEY [ABSENCE-THUNK] => VALUE | ||||
|   KEY can be either a symbol or an integer. | ||||
| If the lookup fails, ABSENCE-THUNK, if given, is evaluated and its value | ||||
| is returned. If ABSENCE-THUNK is omitted, the return value on failure | ||||
| is #f. | ||||
| 
 | ||||
| tiff-directory-get-as-symbol TIFF-DIRECTORY KEY [ABSENCE-THUNK] => VALUE | ||||
|   KEY must be a symbol. | ||||
| If it is possible, the VALUE is returned as a symbol, as translated | ||||
| by the tagdict. | ||||
| 
 | ||||
| tiff-directory-fold-left TIFF-DIRECTORY FOLD-FUNCTION SEED-VALUE | ||||
|             ... => seed-value ... | ||||
|   The fold function receives a tiff-directory-entry as a value | ||||
| 
 | ||||
| read-tiff-file:: EPORT [PRIVATE-TAGDICT] -> TIFF-DIRECTORY | ||||
| print-tiff-directory:: TIFF-DIRECTORY OPORT -> UNSPECIFIED | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ** Usage example: tiff prober | ||||
| 
 | ||||
| The scripts probe-tiff and equivalently tiff-prober.scm read a TIFF file | ||||
| and print out its directory (as well as values of a few "important" tags). | ||||
| The scripts (or script headers) assume that the executable scsh resides | ||||
| in /usr/local/bin, and that the environment variable SCSH_LIB_DIRS lists | ||||
| the sunterlib directory with the config file sunterlib.scm; cf. the reference | ||||
| manual about "Running Scsh\Scsh command-line switches\Switches". | ||||
| 
 | ||||
| Usage | ||||
|         probe-tiff tiff-file1 ... | ||||
| or | ||||
|         tiff-prober.scm tiff-file1 ... | ||||
| 
 | ||||
| Structure tiff-prober exports the entry point for the scripts: | ||||
| 
 | ||||
| tiff-prober ARGV => UNSPECIFIED | ||||
| Call, for instance, (tiff-prober '("foo" "bsp.tiff")). | ||||
| 
 | ||||
| 
 | ||||
| ** Validating the library | ||||
| 
 | ||||
| The valdidating code is in sunterlib/scsh/tiff/vtiff.scm and assumes that | ||||
| the tiffed GNU logo sunterlib/tiff/gnu-head-sm.tif resides in the working | ||||
| directory.  In that situation you may go | ||||
|                                           ,in tiff-testbed  ; and | ||||
|                                           ,load vtiff.scm | ||||
| Alternatively make sure that the env variable SCSH_LIB_DIRS lists the | ||||
| directory with sunterlib.scm (just as for the tiff prober, see above) | ||||
| and run vtiff.scm as script. | ||||
| 
 | ||||
|                                    oOo | ||||
| 
 | ||||
|  | @ -0,0 +1,51 @@ | |||
| ; | ||||
| ; COUT, CERR, NL, ++ from Oleg Kiselyov's myenv.scm | ||||
| ; changes: | ||||
| ;   ##stderr --> (error-output-port) | ||||
| ;   ++ via define-syntax and + | ||||
| 
 | ||||
| (define-syntax define-structure | ||||
|   (lambda (form rename name=) | ||||
|     (let ((name (cadr form))            ; Symbol | Generated-Name | ||||
|           (fields (cddr form))          ; Proper-List(S. | G.N.) | ||||
|           (concat (lambda (symbols)     ; Proper-List(S.) -> S. | ||||
|                     (string->symbol | ||||
|                      (apply string-append | ||||
|                             (map symbol->string symbols)))))) | ||||
| 
 | ||||
|       `(,(rename 'define-record-type) | ||||
|         ,name | ||||
|         (,(concat `(make- ,name)) ,@fields) | ||||
|         ,(concat `(,name ?)) | ||||
|         ,@(map (lambda (field) | ||||
|                  `(,field | ||||
|                    ,(concat `(,name - ,field)) | ||||
|                    ,(concat `(,name - ,field -set!)))) | ||||
|                fields)) | ||||
|       ))) | ||||
| 
 | ||||
| 
 | ||||
| ; like cout << arguments << args | ||||
| ; where argument can be any Scheme object. If it's a procedure | ||||
| ; (without args) it's executed rather than printed (like newline) | ||||
| 
 | ||||
| (define (cout . args) | ||||
|   (for-each (lambda (x) | ||||
|               (if (procedure? x) (x) (display x))) | ||||
|             args)) | ||||
| 
 | ||||
| (define (cerr . args) | ||||
|   (for-each (lambda (x) | ||||
|               (if (procedure? x) | ||||
|                   (x (error-output-port)) | ||||
|                   (display x (error-output-port)))) | ||||
|             args)) | ||||
| 
 | ||||
| (define nl (string #\newline)) | ||||
| 
 | ||||
| 
 | ||||
| ;; read-only increment | ||||
| (define-syntax ++ | ||||
|   (syntax-rules () | ||||
|     ((++ x) | ||||
|      (+ 1 x)))) | ||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,79 @@ | |||
| ; taken and adapted from Oleg Kiselyov's tiff-prober.scm | ||||
| ; Changes | ||||
| ;   ##fixnumm.logior --> bitwise-ior | ||||
| ;   ##fixnum.shl --> arithmetic-shift | ||||
| ;   make-endian-port : switches off buffering | ||||
| ; To be evaluated in an environment binding | ||||
| ;   DEFINE-STRUCTURE to the required syntax (like CL's DEFSTRUCT) | ||||
| ;   CHAR->INTEGER to CHAR->ASCII | ||||
| 
 | ||||
| (define-structure endian-port port msb-first?) | ||||
| (set! make-endian-port | ||||
|       (let ((really-make-endian-port make-endian-port)) | ||||
|         (lambda (port msb-first?) | ||||
|           (set-port-buffering port bufpol/none) ; work around SEEK bug | ||||
|           (really-make-endian-port port msb-first?)))) | ||||
| 
 | ||||
| (define (close-endian-port eport) | ||||
|   (close-input-port (endian-port-port eport))) | ||||
| 
 | ||||
| ; endian-port-set-bigendian! EPORT -> UNSPECIFIED | ||||
| (define (endian-port-set-bigendian! eport) | ||||
|   (endian-port-msb-first?-set! eport #t)) | ||||
| 
 | ||||
| ;   endian-port-set-littlendian! EPORT -> UNSPECIFIED | ||||
| (define (endian-port-set-littlendian! eport) | ||||
|   (endian-port-msb-first?-set! eport #f)) | ||||
| 
 | ||||
| ;   endian-port-read-int1:: PORT -> UINTEGER (byte) | ||||
| (define (endian-port-read-int1 eport) | ||||
|   (let ((c (read-char (endian-port-port eport)))) | ||||
|     (if (eof-object? c) (error "unexpected EOF") | ||||
|       (char->integer c))))		; Gambit-specific. Need read-byte | ||||
|                                         ; sunterlib: c->i bound to char->ascii | ||||
| 
 | ||||
| ;   endian-port-read-int2:: PORT -> UINTEGER | ||||
| (define (endian-port-read-int2 eport) | ||||
|   (let* ((c1 (endian-port-read-int1 eport)) | ||||
| 	 (c2 (endian-port-read-int1 eport))) | ||||
|     (if (endian-port-msb-first? eport) | ||||
|        (bitwise-ior (arithmetic-shift c1 8) c2) ;(+ (* c1 256) c2) | ||||
|        (bitwise-ior (arithmetic-shift c2 8) c1) ;(+ (* c2 256) c1) | ||||
|       ))) | ||||
| 
 | ||||
| ;   endian-port-read-int4:: PORT -> UINTEGER | ||||
| (define (endian-port-read-int4 eport) | ||||
|   (let* ((c1 (endian-port-read-int1 eport)) | ||||
| 	 (c2 (endian-port-read-int1 eport)) | ||||
| 	 (c3 (endian-port-read-int1 eport)) | ||||
| 	 (c4 (endian-port-read-int1 eport))) | ||||
|     (if (endian-port-msb-first? eport) | ||||
|       ;; (+ c4 (* 256 (+ c3 (* 256 (+ c2 (* 256 c1)))))) | ||||
|       (if (< c1 64)			; The int4 will fit into a fixnum | ||||
| 	(bitwise-ior | ||||
| 	  (arithmetic-shift | ||||
| 	    (bitwise-ior | ||||
| 	      (arithmetic-shift | ||||
| 		(bitwise-ior (arithmetic-shift c1 8) c2) 8) c3) 8) c4) | ||||
| 	(+ (* 256			; The multiplication will make a bignum | ||||
| 	     (bitwise-ior | ||||
| 	       (arithmetic-shift | ||||
| 		 (bitwise-ior (arithmetic-shift c1 8) c2) 8) c3)) | ||||
| 	  c4)) | ||||
|       ;; (+ c1 (* 256 (+ c2 (* 256 (+ c3 (* 256 c4)))))) | ||||
|       ; c4 is the most-significant byte | ||||
|       (if (< c4 64) | ||||
| 	(bitwise-ior | ||||
| 	  (arithmetic-shift | ||||
| 	    (bitwise-ior | ||||
| 	      (arithmetic-shift | ||||
| 		(bitwise-ior (arithmetic-shift c4 8) c3) 8) c2) 8) c1) | ||||
| 	(+ (* 256 | ||||
| 	     (bitwise-ior | ||||
| 	       (arithmetic-shift | ||||
| 		 (bitwise-ior (arithmetic-shift c4 8) c3) 8) c2)) | ||||
| 	  c1))))) | ||||
| 
 | ||||
| ;   endian-port-setpos PORT INTEGER -> UNSPECIFIED | ||||
| (define (endian-port-setpos eport pos) | ||||
|   (OS:fseek-abs (endian-port-port eport) pos)) | ||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,78 @@ | |||
| (define tifflet-face | ||||
|   (export read-tiff-file | ||||
|           print-tiff-directory | ||||
|           tiff-directory-get | ||||
|           tiff-directory-get-as-symbol | ||||
|    )) | ||||
| 
 | ||||
| (define tifftag-face | ||||
|   (export tagdict-get-by-name | ||||
|           tagdict-get-by-num | ||||
|           tagdict-tagval-get-by-name | ||||
|           tagdict-tagval-get-by-num | ||||
|           make-tagdict | ||||
|           tagdict? | ||||
|           tagdict-add-all | ||||
|           tiff-standard-tagdict | ||||
|           )) | ||||
| 
 | ||||
| (define tiffdir-face | ||||
|   (export tiff-directory? | ||||
|           tiff-directory-size | ||||
|           tiff-directory-empty? | ||||
|           tiff-directory-get | ||||
|           tiff-directory-get-as-symbol | ||||
|           read-tiff-file | ||||
|           print-tiff-directory | ||||
|           tiff-directory-fold-left | ||||
|           tiff-dir-entry? | ||||
|           tiff-dir-entry-tag | ||||
|           tiff-dir-entry-type | ||||
|           tiff-dir-entry-count | ||||
|           tiff-dir-entry-val-offset | ||||
|           tiff-dir-entry-value | ||||
|           print-tiff-dir-entry | ||||
|           )) | ||||
| 
 | ||||
| (define tiff-prober-face | ||||
|   (export tiff-prober)) | ||||
| 
 | ||||
| (define-interface endian-face | ||||
|   (export make-endian-port | ||||
|           close-endian-port | ||||
|           endian-port-set-bigendian! | ||||
|           endian-port-set-littlendian! | ||||
|           endian-port-read-int1 | ||||
|           endian-port-read-int2 | ||||
|           endian-port-read-int4 | ||||
|           endian-port-setpos)) | ||||
| 
 | ||||
| ;;; | ||||
| 
 | ||||
| (define-interface ersatz-srfi-4-face | ||||
|   (export u8vector? | ||||
|           u8vector  make-u8vector | ||||
|           u8vector-length | ||||
|           u8vector-ref u8vector-set! | ||||
|           u8vector->list list->u8vector | ||||
| 
 | ||||
|           u16vector? | ||||
|           u16vector  make-u16vector | ||||
|           u16vector-length | ||||
|           u16vector-ref u16vector-set! | ||||
|           u16vector->list list->u16vector | ||||
| 
 | ||||
|           u32vector? | ||||
|           u32vector  make-u32vector | ||||
|           u32vector-length | ||||
|           u32vector-ref u32vector-set! | ||||
|           u32vector->list list->u32vector | ||||
|           )) | ||||
| 
 | ||||
| 
 | ||||
| (define-interface tiff-helpers-face | ||||
|   (export (define-structure :syntax) | ||||
|           (++ :syntax) | ||||
|           cerr cout nl)) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -0,0 +1,79 @@ | |||
| (define-structures | ||||
|   ((tifflet tifflet-face) | ||||
|    (tiff (compound-interface tifftag-face tiffdir-face))) | ||||
|   (open tiff-helpers endian | ||||
|         krims                           ; assert | ||||
|         (modify sequence-lib (rename (sequence-any any?)))  ; any? | ||||
|         ascii                           ; ascii->char | ||||
|         srfi-11                         ; let*-values | ||||
|         srfi-23                         ; error | ||||
|         ersatz-srfi-4 | ||||
|         scheme) | ||||
|   (files tiff)) | ||||
| 
 | ||||
| (define-structure endian endian-face | ||||
|   (open tiff-helpers                    ; define-structure | ||||
|         ascii                           ; char->ascii | ||||
|         srfi-23                         ; error | ||||
|         (modify scheme-with-scsh (rename (seek OS:fseek-abs)))) | ||||
|                                         ; seek, bit-ops, buffer policy | ||||
|   (begin (define char->integer char->ascii)) | ||||
|   (files endian)) | ||||
| 
 | ||||
| (define-structure tiff-helpers tiff-helpers-face | ||||
|   (open srfi-9 | ||||
|         srfi-23                         ; error | ||||
|         scheme-with-scsh                ; error-output-port | ||||
|         ) | ||||
|   (for-syntax (open scheme)) | ||||
|   (files aux)) | ||||
| 
 | ||||
| (define-structure ersatz-srfi-4 ersatz-srfi-4-face | ||||
|   (open (modify scheme (alias (vector? u8vector?) | ||||
|                               (vector u8vector) | ||||
|                               (make-vector make-u8vector) | ||||
|                               (vector-length u8vector-length) | ||||
|                               (vector-ref u8vector-ref) | ||||
|                               (vector-set! u8vector-set!) | ||||
|                               (vector->list u8vector->list) | ||||
|                               (list->vector list->u8vector) | ||||
| 
 | ||||
|                               (vector? u16vector?) | ||||
|                               (vector u16vector) | ||||
|                               (make-vector make-u16vector) | ||||
|                               (vector-length u16vector-length) | ||||
|                               (vector-ref u16vector-ref) | ||||
|                               (vector-set! u16vector-set!) | ||||
|                               (vector->list u16vector->list) | ||||
|                               (list->vector list->u16vector) | ||||
| 
 | ||||
|                               (vector? u32vector?) | ||||
|                               (vector u32vector) | ||||
|                               (make-vector make-u32vector) | ||||
|                               (vector-length u32vector-length) | ||||
|                               (vector-ref u32vector-ref) | ||||
|                               (vector-set! u32vector-set!) | ||||
|                               (vector->list u32vector->list) | ||||
|                               (list->vector list->u32vector) | ||||
|                               )))) | ||||
| 
 | ||||
| (define-structure tiff-prober tiff-prober-face | ||||
|   (open tifflet | ||||
|         endian                          ; make-endian-port | ||||
|         tiff-helpers                    ; cout cerr nl | ||||
|         scheme-with-scsh                ; scsh for open-input-file | ||||
|         ) | ||||
|   (begin (define (exit) #f))            ; good enough | ||||
|   (files tiff-prober)) | ||||
| 
 | ||||
| (define-structure tiff-testbed (export ) | ||||
|   (open tiff | ||||
|         endian                          ; endian ports | ||||
|         tiff-helpers                    ; cerr nl ++ | ||||
|         krims                           ; assert | ||||
|         ersatz-srfi-4                   ; fake uniform vectors | ||||
|         srfi-11                         ; let-values* | ||||
|         scheme-with-scsh | ||||
|         ) | ||||
|   (begin )) | ||||
| 
 | ||||
|  | @ -0,0 +1,3 @@ | |||
| #! /usr/local/bin/scsh \ | ||||
| -e tiff-prober -ll sunterlib.scm -o tiff-prober -s | ||||
| !# | ||||
|  | @ -0,0 +1,55 @@ | |||
| #! /usr/local/bin/scsh \ | ||||
| -e tiff-prober -ll sunterlib.scm -o tiff -o tiff-helpers -o endian -s | ||||
| !# | ||||
| 
 | ||||
| ;**************************************************************************** | ||||
| ;				TIFF prober | ||||
| ; | ||||
| ; This code reads a TIFF file and prints out its directory (as well as values | ||||
| ; of a few "important" tags) | ||||
| ; Usage | ||||
| ;	tiff-prober tiff-file1... | ||||
| ; Derived from Oleg Kiselyov's tiff-prober.scm 2.0 2003/10/04 02:35:30 | ||||
| ; Changes for the sunterlib | ||||
| ;   procedural wrapper TIFF-PROBER as entry point | ||||
| ;   argv as parameter | ||||
| ;   endian ports moved to endian.scm | ||||
| 
 | ||||
| (define (tiff-prober argv-s) | ||||
|   (let | ||||
|     ((help | ||||
|       (lambda () | ||||
|         (cerr nl nl "print information about TIFF file(s)" nl) | ||||
|         (cerr nl "Usage") | ||||
|         (cerr nl "       tiff-prober tiff-file1...") | ||||
|         (cerr nl nl "Example:") | ||||
|         (cerr nl "       tiff-prober im1.tiff im2.tiff" nl nl) | ||||
|         (exit))) | ||||
|      ) | ||||
|                           ; (car argv-s) is program's name, as usual | ||||
|     (if (or (null? argv-s) (null? (cdr argv-s))) | ||||
|       (help))		; at least one argument, besides argv[0], is expected | ||||
|     (for-each | ||||
|       (lambda (file-name) | ||||
|         (cout nl nl "Analyzing TIFF file " file-name "..." nl) | ||||
|         (let* ((eport (make-endian-port (open-input-file file-name) #t)) | ||||
|                (tiff-dict (read-tiff-file eport)) | ||||
|                (not-spec (lambda () "*NOT SPECIFIED*"))) | ||||
|           (print-tiff-directory tiff-dict (current-output-port)) | ||||
|           (cout nl "image width:    " | ||||
|             (tiff-directory-get tiff-dict 'TIFFTAG:IMAGEWIDTH not-spec)) | ||||
|           (cout nl "image height:   " | ||||
|             (tiff-directory-get tiff-dict 'TIFFTAG:IMAGELENGTH not-spec)) | ||||
|           (cout nl "image depth:    " | ||||
|             (tiff-directory-get tiff-dict 'TIFFTAG:BITSPERSAMPLE not-spec)) | ||||
|           (cout nl "document name:  " | ||||
|             (tiff-directory-get tiff-dict 'TIFFTAG:DOCUMENTNAME not-spec)) | ||||
|           (cout nl "image description: " nl "  " | ||||
|             (tiff-directory-get tiff-dict 'TIFFTAG:IMAGEDESCRIPTION not-spec)) | ||||
|           (cout nl "time stamp:     " | ||||
|             (tiff-directory-get tiff-dict 'TIFFTAG:DATETIME not-spec)) | ||||
|           (cout nl "compression:    " | ||||
|             (tiff-directory-get-as-symbol tiff-dict | ||||
|               'TIFFTAG:COMPRESSION not-spec)) | ||||
|           (cout nl nl))) | ||||
|       (cdr argv-s))) ) | ||||
|  | @ -0,0 +1,774 @@ | |||
| ;**************************************************************************** | ||||
| ; | ||||
| ;                     Tag Image File Format (TIFF) | ||||
| ; | ||||
| ; | ||||
| ; Tiff tag definitions were borrowed from: | ||||
| ;      > Copyright (c) 1988, 1990 by Sam Leffler. | ||||
| ;      > All rights reserved. | ||||
| ;      > | ||||
| ;      > This file is provided for unrestricted use provided that this | ||||
| ;      > legend is included on all tape media and as a part of the | ||||
| ;      > software program in whole or part.  Users may copy, modify or | ||||
| ;      > distribute this file at will. | ||||
| ;      > | ||||
| ;      > Based on Rev 5.0 from: | ||||
| ;      > Developer's Desk              Window Marketing Group | ||||
| ;      > Aldus Corporation             Microsoft Corporation | ||||
| ;      > 411 First Ave. South          16011 NE 36th Way | ||||
| ;      > Suite 200                     Box 97017 | ||||
| ;      > Seattle, WA  98104            Redmond, WA  98073-9717 | ||||
| ;      > 206-622-5500                  206-882-8080 | ||||
| ; | ||||
| ; Now updated for TIFF 6 | ||||
| ; http://www.wotsit.org/download.asp?f=tiff6 | ||||
| ; | ||||
| ; We rely on an ENDIAN-PORT | ||||
| ; A port with the following operations | ||||
| ;   endian-port-set-bigendian!::   PORT -> UNSPECIFIED | ||||
| ;   endian-port-set-littlendian!:: PORT -> UNSPECIFIED | ||||
| ;   endian-port-read-int1:: PORT -> UINTEGER (byte) | ||||
| ;   endian-port-read-int2:: PORT -> UINTEGER | ||||
| ;   endian-port-read-int4:: PORT -> UINTEGER | ||||
| ;   endian-port-setpos PORT INTEGER -> UNSPECIFIED | ||||
| ; | ||||
| ; Also needed SRFIs: SRFI-4 (uniform vectors), SRFI-9 (records) | ||||
| ; Actually, we're using structures, which can be translated to SRFI-9 | ||||
| ; records. | ||||
| ; | ||||
| ; Derived from Oleg Kiselyov's tiff.scm 2.0 2003/09/29 20:05:12 | ||||
| ; Changes: | ||||
| ;   make-define-env, define-macro --> explicit-renaming | ||||
| 
 | ||||
| 
 | ||||
| ;------------------------------------------------------------------------ | ||||
| ;                              TIFF file header | ||||
| ; It is always written at the very beginning of the TIFF file | ||||
| 
 | ||||
| ;  unsigned short magic;         // magic number (defines byte order) | ||||
| ;  unsigned short version;       // TIFF version number | ||||
| ;  unsigned long  diroffset;     // byte offset to the first directory | ||||
| 
 | ||||
| (define TIFF:BIGENDIAN-magic    #x4d4d)		; 'MM' | ||||
| (define TIFF:LITTLEENDIAN-magic #x4949)		; 'II' | ||||
| (define TIFF:VERSION 42) | ||||
| 
 | ||||
| ; procedure: TIFF:read-header ENDIAN-PORT -> OFFSET | ||||
| ; | ||||
| ; Reads and checks the TIFF header, sets the reader to | ||||
| ; the appropriate little/big endian mode and returns the byte | ||||
| ; offset to the first TIFF directory | ||||
| (define (TIFF:read-header eport) | ||||
|   (let ((magic-word (endian-port-read-int2 eport))) | ||||
|     (cond | ||||
|       ((= magic-word TIFF:BIGENDIAN-magic) | ||||
|        (endian-port-set-bigendian! eport)) | ||||
|       ((= magic-word TIFF:LITTLEENDIAN-magic) | ||||
|        (endian-port-set-littlendian! eport)) | ||||
|       (else (error "invalid magic word 0x" (number->string magic-word 16) | ||||
|      	       "of the TIFF file")))) | ||||
|   (let ((version (endian-port-read-int2 eport))) | ||||
|     (if (not (= version TIFF:VERSION)) | ||||
|       (error "TIFF file version " version "differs from the standard " | ||||
|       	    TIFF:VERSION))) | ||||
|   (endian-port-read-int4 eport)) | ||||
| 
 | ||||
| ;------------------------------------------------------------------------ | ||||
| ; 		    TIFF tags: codes and values | ||||
| ; | ||||
| ; A tag dictionary, tagdict, structure helps translate between | ||||
| ; tag-symbols and their numerical values. | ||||
| ; | ||||
| ; procedure: tagdict-get-by-name TAGDICT TAG-NAME -> INT | ||||
| ; where TAG-NAME is a symbol. | ||||
| ; Translate a symbolic representation of a TIFF tag into a numeric | ||||
| ; representation. | ||||
| ; An error is raised if the lookup fails. | ||||
| ; | ||||
| ; procedure: tagdict-get-by-num TAGDICT INT -> TAG-NAME or #f | ||||
| ; Translate from a numeric tag value to a symbolic representation, | ||||
| ; if it exists. Return #f otherwise. | ||||
| ; | ||||
| ; procedure: tagdict-tagval-get-by-name TAGDICT TAG-NAME VAL-NAME -> INT | ||||
| ; where VAL-NAME is a symbol. | ||||
| ; Translate from the symbolic representation of a value associated | ||||
| ; with TAG-NAME in the TIFF directory, into the numeric representation. | ||||
| ; An error is raised if the lookup fails. | ||||
| ; | ||||
| ; procedure: tagdict-tagval-get-by-num TAGDICT TAG-NAME INT -> VAL-NAME or #f | ||||
| ; Translate from a numeric value associated with TAG-NAME in the TIFF | ||||
| ; directory to a symbolic representation, if it exists. Return #f | ||||
| ; otherwise. | ||||
| ; | ||||
| ; procedure: make-tagdict ((TAG-NAME INT (VAL-NAME . INT) ...) ...) | ||||
| ; Build a tag dictionary | ||||
| ; | ||||
| ; procedure: tagdict? TAGDICT -> BOOL | ||||
| ; | ||||
| ; procedure: tagdict-add-all DEST-DICT SRC-DICT -> DEST-DICT | ||||
| ; Join two dictionaries | ||||
| ; | ||||
| ; The variable tiff-standard-tagdict is initialized to the dictionary | ||||
| ; of standard TIFF tags. | ||||
| 
 | ||||
| ; Usage scenario: | ||||
| ;    (tagdict-get-by-name  tiff-standard-tagdict 'TIFFTAG:IMAGEWIDTH) => 256 | ||||
| ;    (tagdict-get-by-num   tiff-standard-tagdict 256) => 'TIFFTAG:IMAGEWIDTH | ||||
| ;    (tagdict-tagval-get-by-name tiff-standard-tagdict | ||||
| ;       'TIFFTAG:COMPRESSION 'LZW) => 5 | ||||
| ;    (tagdict-tagval-get-by-num  tiff-standard-tagdict | ||||
| ;       'TIFFTAG:COMPRESSION 5) => 'LZW | ||||
| ; | ||||
| ;    (define extended-tagdict | ||||
| ;       (tagdict-add-all tiff-standard-tagdict | ||||
| ;          (make-tagdict | ||||
| ;		'((WAupper_left_lat 33004) | ||||
| ;		  (WAhemisphere 33003 (North . 1) (South . 2)))))) | ||||
| 
 | ||||
| (define-structure p-tiff-tag-dict table) | ||||
| 
 | ||||
| 
 | ||||
| (define tagdict? p-tiff-tag-dict?) | ||||
| 
 | ||||
| (define (make-tagdict args) | ||||
|   (for-each				; error-check each dict association | ||||
|     (lambda (arg) | ||||
|       (or | ||||
| 	(and | ||||
| 	  (pair? arg) | ||||
| 	  (list? arg) | ||||
| 	  (symbol? (car arg)) | ||||
| 	  (integer? (cadr arg))) | ||||
| 	(error "make-tagdict: bad association to add: " arg)) | ||||
|       (for-each | ||||
| 	(lambda (val-assoc) | ||||
| 	  (or | ||||
| 	    (and | ||||
| 	      (pair? val-assoc) | ||||
| 	      (symbol? (car val-assoc)) | ||||
| 	      (integer? (cdr val-assoc))) | ||||
| 	    (error "make-tagdict: bad tag value association: " val-assoc))) | ||||
| 	(cddr arg))) | ||||
|     args) | ||||
|   (make-p-tiff-tag-dict args)) | ||||
| 
 | ||||
| 
 | ||||
| ; procedure: tagdict-add-all DEST-DICT SRC-DICT -> DEST-DICT | ||||
| ; Join two dictionaries | ||||
| (define (tagdict-add-all dest-dict src-dict) | ||||
|   (assert (tagdict? dest-dict) (tagdict? src-dict)) | ||||
|   (make-p-tiff-tag-dict | ||||
|     (append (p-tiff-tag-dict-table dest-dict) | ||||
|             (p-tiff-tag-dict-table src-dict)))) | ||||
| 
 | ||||
| ; procedure: tagdict-get-by-name TAGDICT TAG-NAME -> INT | ||||
| ; An error is raised if the lookup fails. | ||||
| 
 | ||||
| (define (tagdict-get-by-name dict tag-name) | ||||
|   (assert (tagdict? dict)) | ||||
|   (cond | ||||
|     ((assq tag-name (p-tiff-tag-dict-table dict)) | ||||
|       => cadr) | ||||
|     (else | ||||
|       (error "tagdict-get-by-name: can't translate: " tag-name)))) | ||||
| 
 | ||||
| 
 | ||||
| ; procedure: tagdict-get-by-num TAGDICT INT -> TAG-NAME or #f | ||||
| (define (tagdict-get-by-num dict tag-int) | ||||
|   (assert (tagdict? dict)) | ||||
|   (any? | ||||
|     (lambda (table-row) | ||||
|       (and (= (cadr table-row) tag-int) (car table-row))) | ||||
|     (p-tiff-tag-dict-table dict))) | ||||
| 
 | ||||
| 
 | ||||
| ; procedure: tagdict-tagval-get-by-name TAGDICT TAG-NAME VAL-NAME -> INT | ||||
| ; An error is raised if the lookup fails. | ||||
| (define (tagdict-tagval-get-by-name dict tag-name val-name) | ||||
|   (assert (tagdict? dict)) | ||||
|   (cond | ||||
|     ((assq tag-name (p-tiff-tag-dict-table dict)) | ||||
|       => (lambda (table-row) | ||||
| 	   (cond | ||||
| 	     ((assq val-name (cddr table-row)) => cdr) | ||||
| 	     (else | ||||
| 	       (error "tagdict-tagval-get-by-name: can't translate " | ||||
| 		 tag-name val-name))))) | ||||
|     (else | ||||
|       (error "tagdict-tagval-get-by-name: unknown tag: " tag-name)))) | ||||
| 
 | ||||
| 
 | ||||
| ; procedure: tagdict-tagval-get-by-num TAGDICT TAG-NAME INT -> VAL-NAME or #f | ||||
| ; Translate from a numeric value associated with TAG-NAME in the | ||||
| ; TIFF directory. | ||||
| (define (tagdict-tagval-get-by-num dict tag-name val-int) | ||||
|   (assert (tagdict? dict)) | ||||
|   (cond | ||||
|     ((assq tag-name (p-tiff-tag-dict-table dict)) | ||||
|       => (lambda (table-row) | ||||
| 	   (any? | ||||
| 	     (lambda (assc) | ||||
| 	       (and (= val-int (cdr assc)) (car assc))) | ||||
| 	     (cddr table-row)))) | ||||
|     (else | ||||
|       (error "tagdict-tagval-get-by-num: unknown tag: " tag-name)))) | ||||
| 
 | ||||
| (define tiff-standard-tagdict | ||||
|   (make-tagdict | ||||
|     '( | ||||
|        (TIFFTAG:SUBFILETYPE 254	        ; subfile data descriptor | ||||
| 	 (TIFFTAG:REDUCEDIMAGE . #x1)	  ; reduced resolution version | ||||
| 	 (TIFFTAG:PAGE . #x2)		  ; one page of many | ||||
| 	 (TIFFTAG:MASK . #x4)) | ||||
|        (TIFFTAG:OSUBFILETYPE 255	; +kind of data in subfile | ||||
|          (TIFFTAG:IMAGE . 1)		  ; full resolution image data | ||||
| 	 (TIFFTAG:REDUCEDIMAGE . 2)	  ; reduced size image data | ||||
| 	 (TIFFTAG:PAGE . 3))		  ; one page of many | ||||
|        (TIFFTAG:IMAGEWIDTH 256)	        ; image width in pixels | ||||
|        (TIFFTAG:IMAGELENGTH 257)	; image height in pixels | ||||
|        (TIFFTAG:BITSPERSAMPLE 258)	; bits per channel (sample) | ||||
| 
 | ||||
|        (TIFFTAG:COMPRESSION 259	        ; data compression technique | ||||
| 	 (NONE . 1)			  ; dump mode | ||||
| 	 (CCITTRLE . 2)		          ; CCITT modified Huffman RLE | ||||
| 	 (CCITTFAX3 . 3)		  ; CCITT Group 3 fax encoding | ||||
| 	 (CCITTFAX4 . 4)		  ; CCITT Group 4 fax encoding | ||||
| 	 (LZW . 5)			  ; Lempel-Ziv  & Welch | ||||
| 	 (NEXT . 32766)		          ; NeXT 2-bit RLE | ||||
| 	 (CCITTRLEW . 32771)		  ; #1 w/ word alignment | ||||
| 	 (PACKBITS . 32773)		  ; Macintosh RLE | ||||
| 	 (THUNDERSCAN . 32809)		  ; ThunderScan RLE | ||||
| 	 (PICIO . 32900)		  ; old Pixar picio RLE | ||||
| 	 (SGIRLE . 32901))		  ; Silicon Graphics RLE | ||||
| 
 | ||||
|        (TIFFTAG:PHOTOMETRIC 262	        ; photometric interpretation | ||||
| 	 (MINISWHITE . 0)		  ; min value is white | ||||
| 	 (MINISBLACK . 1)		  ; min value is black | ||||
| 	 (RGB . 2)			  ; RGB color model | ||||
| 	 (PALETTE . 3)			  ; color map indexed | ||||
| 	 (MASK . 4)			  ; holdout mask | ||||
| 	 (DEPTH . 32768))		  ; z-depth data | ||||
| 
 | ||||
|        (TIFFTAG:THRESHOLDING 263	; +thresholding used on data | ||||
| 	 (BILEVEL . 1)			  ; b&w art scan | ||||
| 	 (HALFTONE . 2)		          ; or dithered scan | ||||
| 	 (ERRORDIFFUSE . 3))		  ; usually floyd-steinberg | ||||
| 
 | ||||
|        (TIFFTAG:CELLWIDTH 264)		; +dithering matrix width | ||||
|        (TIFFTAG:CELLLENGTH 265)	        ; +dithering matrix height | ||||
|        (TIFFTAG:FILLORDER 266		; +data order within a byte | ||||
| 	 (MSB2LSB . 1)			  ; most significant -> least | ||||
| 	 (LSB2MSB . 2))		          ; least significant -> most | ||||
| 
 | ||||
|        (TIFFTAG:DOCUMENTNAME 269)	; name of doc. image is from | ||||
|        (TIFFTAG:IMAGEDESCRIPTION 270)	; info about image | ||||
|        (TIFFTAG:MAKE 271)		; scanner manufacturer name | ||||
|        (TIFFTAG:MODEL 272)		; scanner model name/number | ||||
|        (TIFFTAG:STRIPOFFSETS 273)	; offsets to data strips | ||||
| 
 | ||||
|        (TIFFTAG:ORIENTATION 274	        ; +image orientation | ||||
| 	 (TOPLEFT . 1)			  ; row 0 top, col 0 lhs | ||||
| 	 (TOPRIGHT . 2)		          ; row 0 top, col 0 rhs | ||||
| 	 (BOTRIGHT . 3)		          ; row 0 bottom, col 0 rhs | ||||
| 	 (BOTLEFT . 4)			  ; row 0 bottom, col 0 lhs | ||||
| 	 (LEFTTOP . 5)			  ; row 0 lhs, col 0 top | ||||
| 	 (RIGHTTOP . 6)		          ; row 0 rhs, col 0 top | ||||
| 	 (RIGHTBOT . 7)		          ; row 0 rhs, col 0 bottom | ||||
| 	 (LEFTBOT . 8))		          ; row 0 lhs, col 0 bottom | ||||
| 
 | ||||
|        (TIFFTAG:SAMPLESPERPIXEL 277)	; samples per pixel | ||||
|        (TIFFTAG:ROWSPERSTRIP 278)	; rows per strip of data | ||||
|        (TIFFTAG:STRIPBYTECOUNTS 279)	; bytes counts for strips | ||||
|        (TIFFTAG:MINSAMPLEVALUE 280)	; +minimum sample value | ||||
|        (TIFFTAG:MAXSAMPLEVALUE 281)	; maximum sample value | ||||
|        (TIFFTAG:XRESOLUTION 282)	; pixels/resolution in x | ||||
|        (TIFFTAG:YRESOLUTION 283)	; pixels/resolution in y | ||||
| 
 | ||||
|        (TIFFTAG:PLANARCONFIG 284	; storage organization | ||||
| 	 (CONTIG . 1)			  ; single image plane | ||||
| 	 (SEPARATE . 2))		  ; separate planes of data | ||||
| 
 | ||||
|        (TIFFTAG:PAGENAME 285)		; page name image is from | ||||
|        (TIFFTAG:XPOSITION 286)		; x page offset of image lhs | ||||
|        (TIFFTAG:YPOSITION 287)		; y page offset of image lhs | ||||
|        (TIFFTAG:FREEOFFSETS 288)	; +byte offset to free block | ||||
|        (TIFFTAG:FREEBYTECOUNTS 289)	; +sizes of free blocks | ||||
| 
 | ||||
|        (TIFFTAG:GRAYRESPONSEUNIT 290	; gray scale curve accuracy | ||||
| 	 (S10 . 1)			  ; tenths of a unit | ||||
| 	 (S100 . 2)			  ; hundredths of a unit | ||||
| 	 (S1000 . 3)			  ; thousandths of a unit | ||||
| 	 (S10000 . 4)			  ; ten-thousandths of a unit | ||||
| 	 (S100000 . 5))		          ; hundred-thousandths | ||||
|        (TIFFTAG:GRAYRESPONSECURVE 291)	; gray scale response curve | ||||
| 
 | ||||
|        (TIFFTAG:GROUP3OPTIONS 292	; 32 flag bits | ||||
| 	 (ENCODING2D . #x1)		  ; 2-dimensional coding | ||||
| 	 (UNCOMPRESSED . #x2)		  ; data not compressed | ||||
| 	 (FILLBITS . #x4))		  ; fill to byte boundary | ||||
|        (TIFFTAG:GROUP4OPTIONS 293	; 32 flag bits | ||||
| 	 (UNCOMPRESSED . #x2))		; data not compressed | ||||
| 
 | ||||
|        (TIFFTAG:RESOLUTIONUNIT 296	; units of resolutions | ||||
| 	 (NONE . 1)			  ; no meaningful units | ||||
| 	 (INCH . 2)			  ; english | ||||
| 	 (CENTIMETER . 3))		  ; metric | ||||
| 
 | ||||
|        (TIFFTAG:PAGENUMBER 297)	        ; page numbers of multi-page | ||||
| 
 | ||||
|        (TIFFTAG:COLORRESPONSEUNIT 300	; color scale curve accuracy | ||||
| 	 (S10 . 1)			  ; tenths of a unit | ||||
| 	 (S100 . 2)			  ; hundredths of a unit | ||||
| 	 (S1000 . 3)			  ; thousandths of a unit | ||||
| 	 (S10000 . 4)			  ; ten-thousandths of a unit | ||||
| 	 (S100000 . 5))			  ; hundred-thousandths | ||||
| 
 | ||||
|        (TIFFTAG:COLORRESPONSECURVE 301); RGB response curve | ||||
|        (TIFFTAG:SOFTWARE 305)		; name & release | ||||
|        (TIFFTAG:DATETIME 306)		; creation date and time | ||||
|        (TIFFTAG:ARTIST 315)		; creator of image | ||||
|        (TIFFTAG:HOSTCOMPUTER 316)	; machine where created | ||||
|        (TIFFTAG:PREDICTOR 317)		; prediction scheme w/ LZW | ||||
|        (TIFFTAG:WHITEPOINT  318)	; image white point | ||||
|        (TIFFTAG:PRIMARYCHROMATICITIES 319) ; primary chromaticities | ||||
|        (TIFFTAG:COLORMAP 320)		; RGB map for pallette image | ||||
|        (TIFFTAG:BADFAXLINES 326)	; lines w/ wrong pixel count | ||||
| 
 | ||||
|        (TIFFTAG:CLEANFAXDATA 327	; regenerated line info | ||||
| 	 (CLEAN . 0)			  ; no errors detected | ||||
| 	 (REGENERATED . 1)		  ; receiver regenerated lines | ||||
| 	 (UNCLEAN . 2))		          ; uncorrected errors exist | ||||
| 
 | ||||
|        (TIFFTAG:CONSECUTIVEBADFAXLINES 328); max consecutive bad lines | ||||
| 
 | ||||
|        (TIFFTAG:MATTEING 32995)	        ; alpha channel is present | ||||
| ))) | ||||
| 
 | ||||
| ;------------------------------------------------------------------------ | ||||
| ; 			TIFF directory entry | ||||
| ; a descriptor of a TIFF "item", which can be image data, document description, | ||||
| ; time stamp, etc, depending on the tag. Thus an entry has the following | ||||
| ; structure: | ||||
| ;  unsigned short tag; | ||||
| ;  unsigned short type;          // data type: byte, short word, etc. | ||||
| ;  unsigned long  count;         // number of items; length in spec | ||||
| ;  unsigned long  val_offset;    // byte offset to field data | ||||
| ; | ||||
| ; The values associated with each entry are disjoint and may appear anywhere | ||||
| ; in the file (so long as they are placed on a word boundary). | ||||
| ; | ||||
| ; Note, If the value takes 4 bytes or less, then it is placed in the offset | ||||
| ; field to save space.  If the value takes less than 4 bytes, it is | ||||
| ; *left*-justified in the offset field. | ||||
| ; Note, that it's always *left* justified (stored in the lower bytes) | ||||
| ; no matter what the byte order (big- or little- endian) is! | ||||
| ; Here's the precise quote from the TIFF 6.0 specification: | ||||
| ; "To save time and space the Value Offset contains the Value instead of | ||||
| ; pointing to the Value if and only if the Value fits into 4 bytes. If | ||||
| ; the Value is shorter than 4 bytes, it is left-justified within the | ||||
| ; 4-byte Value Offset, i.e., stored in the lower- numbered | ||||
| ; bytes. Whether the Value fits within 4 bytes is determined by the Type | ||||
| ; and Count of the field." | ||||
| 
 | ||||
| 
 | ||||
| ; Could be easily implemented as a syntax-rule | ||||
| (define-syntax make-define-env | ||||
|   (lambda (form rename name=) | ||||
|     (let ((env-name (cadr form)) | ||||
|           (associations (cddr form)) | ||||
|           (%begin (rename 'begin)) | ||||
|           (%define (rename 'define))) | ||||
|       `(,%begin | ||||
|         (,%define ,env-name ',associations) | ||||
|         ,@(map | ||||
|            (lambda (assc)			; (name val . other fields) | ||||
|              `(,%define ,(car assc) ,(cadr assc))) | ||||
|            associations))))) | ||||
| 
 | ||||
| 			; values of the 'data type' field | ||||
| (make-define-env | ||||
|   TIFF:types | ||||
|   (TIFF:type-byte  1 "byte")	        ; 8-bit unsigned integer | ||||
|   (TIFF:type-ascii 2 "ascii str")	; 8-bit bytes w/ last byte null | ||||
|   (TIFF:type-short 3 "short")	        ; 16-bit unsigned integer | ||||
|   (TIFF:type-long  4 "long")	        ; 32-bit unsigned integer | ||||
|   (TIFF:type-rational  5 "rational")    ; 64-bit fractional (numer+denominator) | ||||
|     				; The following was added in TIFF 6.0 | ||||
|   (TIFF:type-sbyte     6 "signed byte") ; 8-bit signed (2s-complement) integer | ||||
|   (TIFF:type-undefined 7 "8-bit chunk") ; An 8-bit byte | ||||
|   (TIFF:type-sshort    8 "signed short"); 16-bit signed (2s-complement) integer | ||||
|   (TIFF:type-slong     9 "signed long")	; 32-bit signed (2s-complement) integer | ||||
|   (TIFF:type-srational 10 "signed rational")    ; two SLONGs (num+denominator) | ||||
|   (TIFF:type-float     11 "IEEE 32-bit float")	; single precision (4-byte) | ||||
|   (TIFF:type-double    12 "IEEE 64-bit double") ; double precision (8-byte) | ||||
| ) | ||||
| 
 | ||||
| (define-structure tiff-dir-entry tag type count val-offset value) | ||||
| 
 | ||||
| ; procedure: TIFF:read-dir-entry EPORT -> TIFF-DIR-ENTRY | ||||
| ; | ||||
| ; This procedure parses the current directory entry and | ||||
| ; returns a tiff-dir-entry structure. EPORT must point to the beginning | ||||
| ; of the entry in the TIFF directory. On exit, EPORT points to the | ||||
| ; next entry or the end of the directory. | ||||
| ; TIFF-DIR-ENTRY contains all the data of the entry, plus a promise | ||||
| ; of entry's value. | ||||
| ; The promise is forced only when the value is specifically requested | ||||
| ; by an object's user. That is, we won't rush to read and make the | ||||
| ; value (which may be pretty big: say the pixel matrix, etc). | ||||
| ; | ||||
| ; The promise closes over the EPORT! | ||||
| ; | ||||
| ; The value of an entry corresponds to its type: character, string, | ||||
| ; exact integer, floating-point number, or a uniform | ||||
| ; (u8, u16, or u32) vector. SRFI-4 is implied. | ||||
| 
 | ||||
| (define (TIFF:read-dir-entry eport) | ||||
|   (let* | ||||
|     ((tag        (endian-port-read-int2 eport)) | ||||
|      (type       (endian-port-read-int2 eport)) | ||||
|      (count      (endian-port-read-int4 eport)) | ||||
|       ; we read the val-offset later. We need to check the size and the type | ||||
|       ; of the datum, because val-offset may contain the value itself, | ||||
|       ; in its lower-numbered bytes, regardless of the big/little endian | ||||
|       ; order! | ||||
|     ) | ||||
| 				; Some conversion procedures | ||||
|     (define (u32->float x)		; unsigned 32-bit int -> IEEE float | ||||
|       (error "u32->float is not yet implemented")) | ||||
|     (define (u32->s32 x)		; unsigned 32-bit int -> signed 32 bit | ||||
|       (if (>= x #x80000000) | ||||
| 	(- x #x100000000) | ||||
| 	x)) | ||||
|     ; (= (u32->s32 #x7fffffff) #x7fffffff) | ||||
|     ; (= (u32->s32 #xffffffff) -1) | ||||
| 
 | ||||
|     (define (u16->s16 x)		; unsigned 16-bit int -> signed 16 bit | ||||
|       (if (>= x #x8000) | ||||
| 	(- x #x10000) x)) | ||||
|     ; (= (u16->s16 32767) 32767) | ||||
|     ; (= (u16->s16 32768) -32768) | ||||
|     ; (= (u16->s16 65535) -1) | ||||
| 
 | ||||
|     (define (u8->s8 x)			; unsigned 8-bit int -> signed 8-bit | ||||
|       (if (>= x #x80) | ||||
| 	(- x #x100) x)) | ||||
|     ; (= (u8->s8 127) 127) | ||||
|     ; (= (u8->s8 128) -128) | ||||
|     ; (= (u8->s8 255) -1) | ||||
| 
 | ||||
|     (define (read-double val-offset) | ||||
|       (error "read-double: not yet implemented")) | ||||
| 
 | ||||
|             		; read an ascii string. Note, the last byte of | ||||
|             		; an ascii string is always zero, which is | ||||
|             		; included in 'count' | ||||
|             		; but we don't need to read it | ||||
|     (define (read-string val-offset) | ||||
|       (assert (= TIFF:type-ascii type) (positive? count)) | ||||
|       (let ((str (make-string (- count 1)))) | ||||
| 	(endian-port-setpos eport val-offset) | ||||
| 	(do ((i 0 (+ 1 i))) ((>= i (- count 1)) str) | ||||
| 	  (string-set! str i | ||||
| 	    (ascii->char (endian-port-read-int1 eport)))))) | ||||
| 
 | ||||
| 
 | ||||
|                		; read an array of 'count' items | ||||
|                		; return a *uniform* vector of read data: | ||||
| 			; u8vector, u16vector or u32vector | ||||
| 			; We roll-out the code for efficiency | ||||
|     (define (read-array val-offset) | ||||
|       (endian-port-setpos eport val-offset) | ||||
|       (cond | ||||
| 	((or (= type TIFF:type-byte) (= type TIFF:type-undefined)) | ||||
| 	  (let ((array (make-u8vector count))) | ||||
| 	    (do ((i 0 (+ 1 i))) ((>= i count) array) | ||||
| 	      (u8vector-set! array i (endian-port-read-int1 eport))))) | ||||
| 	((= type TIFF:type-short) | ||||
| 	  (let ((array (make-u16vector count))) | ||||
| 	    (do ((i 0 (+ 1 i))) ((>= i count) array) | ||||
| 	      (u16vector-set! array i (endian-port-read-int2 eport))))) | ||||
| 	((= type TIFF:type-long) | ||||
| 	  (let ((array (make-u32vector count))) | ||||
| 	    (do ((i 0 (+ 1 i))) ((>= i count) array) | ||||
| 	      (u32vector-set! array i (endian-port-read-int4 eport))))) | ||||
| 	(else (error "don't know how to read an array " | ||||
| 		"of type " type)))) | ||||
| 
 | ||||
|     ; Now we need to figure out if val-offset contains the offset | ||||
|     ; or the value (or a part of the value). If val-offset contains the | ||||
|     ; value, we read it in val-offset and make the value a trivial promise | ||||
|     ; (delay val-offset). | ||||
|     ; If val-offset is an offset, then value is a non-trivial promise | ||||
|     ; (which closes over EPORT). | ||||
|     (assert (positive? count)) | ||||
|     (let*-values | ||||
|       (((val-offset value) | ||||
| 	 (cond | ||||
| 	   ((> count 4) 		; for sure, val-offset is an offset | ||||
| 	     (let ((offset (endian-port-read-int4 eport))) | ||||
| 	       (if (= type TIFF:type-ascii) | ||||
| 		 (values offset (delay (read-string offset))) | ||||
| 		 (values offset (delay (read-array offset)))))) | ||||
| 	   ((> count 1)			; the iffy case | ||||
| 	     (cond | ||||
| 	       ((and (= count 2) (= type TIFF:type-short)) | ||||
| 		 (let* ((v1 (endian-port-read-int2 eport)) | ||||
| 			(v2 (endian-port-read-int2 eport)) | ||||
| 			(v  (u16vector v1 v2))) | ||||
| 		   (values v (delay v)))) | ||||
| 	       ((and (= count 2) (= type TIFF:type-ascii)) ; 1-char string | ||||
| 		 (let ((v | ||||
| 			 (string (ascii->char (endian-port-read-int1 eport))))) | ||||
| 		   (endian-port-read-int1 eport) ; don't read '\0' | ||||
| 		   (endian-port-read-int1 eport) ; skip two more zeros: | ||||
| 		   (endian-port-read-int1 eport) ; padding | ||||
| 		   (values v (delay v)))) | ||||
| 	       ((and (= count 2) (or (= type TIFF:type-byte) | ||||
| 				     (= type TIFF:type-undefined))) | ||||
| 		 (let* ((v1 (endian-port-read-int1 eport)) | ||||
| 			(v2 (endian-port-read-int1 eport)) | ||||
| 			(v  (u8vector v1 v2))) | ||||
| 		   (endian-port-read-int1 eport) ; skip two more zeros: | ||||
| 		   (endian-port-read-int1 eport) ; padding | ||||
| 		   (values v (delay v)))) | ||||
| 	       ((and (= count 3) (= type TIFF:type-ascii)) ; 2-char string | ||||
| 		 (let* ((v1 (endian-port-read-int1 eport)) | ||||
| 			(v2 (endian-port-read-int1 eport)) | ||||
| 			(v  (string (ascii->char v1) (ascii->char v2)))) | ||||
| 		   (endian-port-read-int1 eport) ; skip two more zeros: | ||||
| 		   (endian-port-read-int1 eport) ; padding | ||||
| 		   (values v (delay v)))) | ||||
| 	       ((and (= count 3) (or (= type TIFF:type-byte) | ||||
| 				     (= type TIFF:type-undefined))) | ||||
| 		 (let* ((v1 (endian-port-read-int1 eport)) | ||||
| 			(v2 (endian-port-read-int1 eport)) | ||||
| 			(v3 (endian-port-read-int1 eport)) | ||||
| 			(v  (u8vector v1 v2 v3))) | ||||
| 		   (endian-port-read-int1 eport) ; skip padding | ||||
| 		   (values v (delay v)))) | ||||
| 	       ((and (= count 4) (= type TIFF:type-ascii)) ; 3-char string | ||||
| 		 (let* ((v1 (endian-port-read-int1 eport)) | ||||
| 			(v2 (endian-port-read-int1 eport)) | ||||
| 			(v3 (endian-port-read-int1 eport)) | ||||
| 			(v  (string (ascii->char v1) (ascii->char v2) | ||||
| 			      (ascii->char v3)))) | ||||
| 		   (endian-port-read-int1 eport) ; skip padding | ||||
| 		   (values v (delay v)))) | ||||
| 	       ((and (= count 4) (or (= type TIFF:type-byte) | ||||
| 				     (= type TIFF:type-undefined))) | ||||
| 		 (let* ((v1 (endian-port-read-int1 eport)) | ||||
| 			(v2 (endian-port-read-int1 eport)) | ||||
| 			(v3 (endian-port-read-int1 eport)) | ||||
| 			(v4 (endian-port-read-int1 eport)) | ||||
| 			(v  (u8vector v1 v2 v3 v4))) | ||||
| 		   (values v (delay v)))) | ||||
| 	       (else | ||||
| 		 (let ((offset (endian-port-read-int4 eport))) | ||||
| 		   (if (= type TIFF:type-ascii) | ||||
| 		     (values offset (delay (read-string offset))) | ||||
| 		     (values offset (delay (read-array offset)))))))) | ||||
| 	   ; Now count is 1 | ||||
| 	   ((or (= type TIFF:type-byte) (= type TIFF:type-undefined) | ||||
| 	        (= type TIFF:type-sbyte)) | ||||
| 	     (let ((v1 (endian-port-read-int1 eport))) | ||||
| 	       (endian-port-read-int1 eport) ; skip the padding | ||||
| 	       (endian-port-read-int1 eport) | ||||
| 	       (endian-port-read-int1 eport) | ||||
| 	       (values v1 (delay | ||||
| 			   (if (= type TIFF:type-sbyte) (u8->s8 v1) v1))))) | ||||
| 	   ((= type TIFF:type-ascii)	; 0-byte string: count=1 for terminator | ||||
| 	     (endian-port-read-int1 eport) ; skip the padding | ||||
| 	     (endian-port-read-int1 eport) | ||||
| 	     (endian-port-read-int1 eport) | ||||
| 	     (endian-port-read-int1 eport) | ||||
| 	     (values "" (delay ""))) | ||||
| 	  ((or (= type TIFF:type-short) (= type TIFF:type-sshort)) | ||||
| 	    (let ((v1 (endian-port-read-int2 eport))) | ||||
| 	      (endian-port-read-int1 eport) ; skip the padding | ||||
| 	      (endian-port-read-int1 eport) | ||||
| 	      (values v1 (delay | ||||
| 			  (if (= type TIFF:type-sshort) (u16->s16 v1) v1))))) | ||||
| 	   ((= type TIFF:type-long) | ||||
| 	     (let ((v1 (endian-port-read-int4 eport))) | ||||
| 	       (values v1 (delay v1)))) | ||||
| 	   ((= type TIFF:type-slong) | ||||
| 	     (let ((v1 (endian-port-read-int4 eport))) | ||||
| 	       (values v1 (delay (u32->s32 v1))))) | ||||
| 	   ((= type TIFF:type-float) | ||||
| 	     (let ((v1 (endian-port-read-int4 eport))) | ||||
| 	       (values v1 (delay (u32->float v1))))) | ||||
| 	   ((= type TIFF:type-double) | ||||
| 	     (let ((offset (endian-port-read-int4 eport))) | ||||
| 	       (values offset (delay (read-double offset))))) | ||||
| 	   ((or (= type TIFF:type-rational) (= type TIFF:type-srational)) | ||||
| 	     (let ((offset (endian-port-read-int4 eport))) | ||||
| 	       (values offset | ||||
| 		 (delay | ||||
| 		   (let* ((_  (endian-port-setpos eport offset)) | ||||
| 			  (v1 (endian-port-read-int4 eport)) | ||||
| 			  (v2 (endian-port-read-int4 eport))) | ||||
| 		     (if (= type TIFF:type-srational) | ||||
| 		       (/ (u32->s32 v1) (u32->s32 v2)) | ||||
| 		       (/ v1 v2))))))) | ||||
| 	   (else (delay (error "unknown data type: " type)))))) | ||||
|       (make-tiff-dir-entry tag type count val-offset value) | ||||
|     ))) | ||||
| 
 | ||||
| ; procedure: print-tiff-dir-entry TIFF-DIR-ENTRY TAGDICT OPORT -> UNSPECIFIED | ||||
| ; | ||||
| ; Print the contents of TIFF-DIR-ENTRY onto the output port OPORT | ||||
| ; using TAGDICT to convert tag identifiers to symbolic names | ||||
| (define (print-tiff-dir-entry tiff-dir-entry tagdict oport) | ||||
|   (define (dspl . args) (for-each (lambda (item) (display item oport)) args)) | ||||
|   (let* ((tag-num (tiff-dir-entry-tag tiff-dir-entry)) | ||||
| 	 (tag-symbol (tagdict-get-by-num tagdict tag-num))) | ||||
|     (dspl | ||||
|       (or tag-symbol | ||||
| 	(string-append "private tag " (number->string tag-num)))) | ||||
|     (dspl  ", count " (tiff-dir-entry-count tiff-dir-entry) | ||||
|            ", type ") | ||||
|     (let ((type-str | ||||
| 	    (any? | ||||
| 	      (lambda (elem) | ||||
| 		(and (= (cadr elem) (tiff-dir-entry-type tiff-dir-entry)) | ||||
| 		  (caddr elem))) | ||||
| 	      TIFF:types))) | ||||
|       (if type-str | ||||
| 	(dspl type-str) | ||||
| 	(dspl "unknown (" (tiff-dir-entry-type tiff-dir-entry) ")"))) | ||||
|     (let ((val-offset (tiff-dir-entry-val-offset tiff-dir-entry))) | ||||
|       (dspl ", value-offset " val-offset) | ||||
|       (if (integer? val-offset) | ||||
| 	(dspl " (0x" (number->string val-offset 16) ")"))) | ||||
|     (dspl nl))) | ||||
| 
 | ||||
| 
 | ||||
| ;------------------------------------------------------------------------ | ||||
| ;                      TIFF Image File Directory | ||||
| ; TIFF directory is a collection of TIFF directory entries. The entries | ||||
| ; are sorted in an ascending order by tag. | ||||
| ; Note, a TIFF file can contain more than one directory (chained together). | ||||
| ; We handle only the first one. | ||||
| ; | ||||
| ; We treat a TIFF image directory somewhat as an ordered, immutable, | ||||
| ; dictionary collection, see SRFI-44. | ||||
| 
 | ||||
| ; http://srfi.schemers.org/srfi-44/srfi-44.html | ||||
| 
 | ||||
| (define-structure tiff-directory entries tagdict) | ||||
| 
 | ||||
| ; ; procedure: collection-name collection => symbol ('%) | ||||
| ; (define (collection-name coll) | ||||
| ;   (and (tiff-directory? coll) 'tiff-directory)) | ||||
| 
 | ||||
| ; ; collection? value => value | ||||
| ; (define collection? tiff-directory?) | ||||
| 
 | ||||
| ; ; procedure: tiff-directory? value => bool | ||||
| ; ; implied by the define-structure | ||||
| 
 | ||||
| ; ; *-size collection => integer | ||||
| (define (tiff-directory-size coll) | ||||
|   (vector-length (tiff-directory-entries coll))) | ||||
| 
 | ||||
| ; (define (mutable-collection? coll) #f) | ||||
| ; (define (dictionary? coll) #t) | ||||
| 
 | ||||
| (define (tiff-directory-empty? coll) | ||||
|   (zero? (vector-length (tiff-directory-entries coll)))) | ||||
| 
 | ||||
| 
 | ||||
| ; tiff-directory-fold-left tiff-directory fold-function seed-value | ||||
| ;             ... => seed-value ... | ||||
| ; The fold function receives a tiff-directory-entry as a value | ||||
| 
 | ||||
| (define (tiff-directory-fold-left coll fn . seeds) | ||||
|   (let ((entries (tiff-directory-entries coll))) | ||||
|     (let loop ((i 0) (seeds seeds)) | ||||
|       (if (>= i (vector-length entries)) | ||||
| 	(apply values seeds) | ||||
| 	(let*-values | ||||
| 	  (((proceed? . seeds) (apply fn (vector-ref entries i) seeds))) | ||||
| 	  (loop (if proceed? (+ 1 i) (vector-length entries)) | ||||
| 	    seeds)))))) | ||||
| 
 | ||||
| ;             procedure: collection-fold-keys-left collection fold-function | ||||
| ;             seed-value ... => seed-value ... | ||||
| ; *-keys->list dictionary => list | ||||
| 
 | ||||
| 
 | ||||
| ; read-tiff-file EPORT [PRIVATE-TAGDICT] => TIFF-DIRECTORY | ||||
| (define (read-tiff-file eport . tag-dict-opt) | ||||
|   (endian-port-setpos eport (TIFF:read-header eport)) | ||||
|   (let ((entries (make-vector (endian-port-read-int2 eport))) | ||||
| 	(tagdict (if (null? tag-dict-opt) | ||||
| 		   tiff-standard-tagdict | ||||
| 		   (tagdict-add-all tiff-standard-tagdict | ||||
| 		     (car tag-dict-opt))))) | ||||
|     (do ((i 0 (+ 1 i))) ((>= i (vector-length entries))) | ||||
|       (vector-set! entries i (TIFF:read-dir-entry eport))) | ||||
|     (if (not (zero? (endian-port-read-int4 eport))) | ||||
|       (cerr "The TIFF file contains several images, only the first one " | ||||
| 	"will be considered" nl)) | ||||
|     (make-tiff-directory entries tagdict))) | ||||
| 
 | ||||
| 
 | ||||
| ; print-tiff-directory TIFF-DIRECTORY OPORT -> UNSPECIFIED | ||||
| (define (print-tiff-directory tiff-directory oport) | ||||
|   (let* | ||||
|     ((entries (tiff-directory-entries tiff-directory)) | ||||
|      (nentries (vector-length entries)) | ||||
|      (tagdict (tiff-directory-tagdict tiff-directory))) | ||||
|     (for-each (lambda (item) (display item oport)) | ||||
|       (list | ||||
| 	"There are " nentries " entries in the TIFF directory" nl | ||||
| 	"they are" nl)) | ||||
|     (do ((i 0 (+ 1 i))) ((>= i nentries)) | ||||
|       (print-tiff-dir-entry (vector-ref entries i) tagdict oport)))) | ||||
| 
 | ||||
| 
 | ||||
| ; *-get dictionary key [absence-thunk] => value | ||||
| ; key can be either a symbol or an integer | ||||
| ; tiff-directory-get TIFF-DIRECTORY KEY [ABSENCE-THUNK] -> VALUE | ||||
| ; If the lookup fails, ABSENCE-THUNK, if given, is evaluated and its value | ||||
| ; is returned. If ABSENCE-THUNK is omitted, the return value on failure | ||||
| ; is #f. | ||||
| (define (tiff-directory-get coll key . default-val) | ||||
|   (let* | ||||
|     ((key | ||||
|        (cond | ||||
| 	 ((symbol? key) | ||||
| 	   (tagdict-get-by-name (tiff-directory-tagdict coll) key)) | ||||
| 	 ((integer? key) key) | ||||
| 	 (else (error "tiff-directory-get: bad type of key: " key)))) | ||||
|       (entry | ||||
|              ; look up the entry in the directory of entries | ||||
|              ; We could have used a binary search... On the other hand, | ||||
|              ; the directory is usually not that big, so that binary | ||||
|              ; search is kind of overkill | ||||
| 	(any? | ||||
| 	  (lambda (curr-elem) | ||||
| 	    (and (= (tiff-dir-entry-tag curr-elem) key) curr-elem)) | ||||
| 	  (tiff-directory-entries coll))) | ||||
|       ) | ||||
|     (if entry | ||||
|       (force (tiff-dir-entry-value entry)) | ||||
|       (and (not (null? default-val)) ((car default-val)))))) | ||||
| 
 | ||||
| 
 | ||||
| ; tiff-directory-get-as-symbol TIFF-DIRECTORY KEY [ABSENCE-THUNK] -> VALUE | ||||
| ; KEY must be a symbol | ||||
| ; If it is possible, the VALUE is returned as a symbol, as translated | ||||
| ; by the tagdict. | ||||
| (define (tiff-directory-get-as-symbol coll key . default-val) | ||||
|   (let ((val (tiff-directory-get coll key))) | ||||
|     (if val | ||||
|       (if (integer? val) | ||||
| 	(let ((val-symbol | ||||
| 		(tagdict-tagval-get-by-num | ||||
| 		  (tiff-directory-tagdict coll) key val))) | ||||
| 	  (or val-symbol val)) | ||||
| 	val)			; val is not an integer: don't translate | ||||
|       (and (not (null? default-val)) ((car default-val))) | ||||
|       ))) | ||||
|  | @ -0,0 +1,176 @@ | |||
| #! /usr/local/bin/scsh \ | ||||
| -ll sunterlib.scm -m tiff-testbed -s | ||||
| !# | ||||
| 
 | ||||
| ;**************************************************************************** | ||||
| ;			Validate the TIFF reading package | ||||
| ; | ||||
| ; We test reading of a known TIFF file, print out its directory. | ||||
| ; We also test an internal consistency of the package. | ||||
| ; | ||||
| ; Derived from vtiff.scm 1.1 2003/09/29 20:41:51 oleg | ||||
| 
 | ||||
| 		; Note: some tests below depend on the exact parameters | ||||
| 		; of the following sample file | ||||
| 		; The file is a GNU logo (from http://www.gnu.org) | ||||
| 		; converted from JPG to TIFF | ||||
| 
 | ||||
| (define sample-tiff-file "gnu-head-sm.tif") | ||||
| 
 | ||||
| (cerr nl "Verifying the TIFF library" nl) | ||||
| 
 | ||||
| (cerr nl "Verifying tagdict operations..." nl) | ||||
| (let () | ||||
|   (assert | ||||
|     (= 256 (tagdict-get-by-name tiff-standard-tagdict 'TIFFTAG:IMAGEWIDTH))) | ||||
|   (assert (eq? 'TIFFTAG:IMAGEWIDTH | ||||
| 	    (tagdict-get-by-num tiff-standard-tagdict 256))) | ||||
|   (assert (eq? #f | ||||
| 	    (tagdict-get-by-num tiff-standard-tagdict 65500))) | ||||
|   (assert (= 5 | ||||
| 	    (tagdict-tagval-get-by-name tiff-standard-tagdict | ||||
| 	      'TIFFTAG:COMPRESSION 'LZW))) | ||||
|   (assert (eq? 'LZW | ||||
| 	    (tagdict-tagval-get-by-num tiff-standard-tagdict | ||||
| 	      'TIFFTAG:COMPRESSION 5))) | ||||
|   (assert (eq? #f | ||||
| 	    (tagdict-tagval-get-by-num tiff-standard-tagdict | ||||
| 	      'TIFFTAG:COMPRESSION 65500))) | ||||
| 
 | ||||
|   (let ((ext-dict | ||||
| 	  (tagdict-add-all tiff-standard-tagdict | ||||
| 	    (make-tagdict | ||||
| 	      '((WAupper_left_lat 33004) | ||||
| 		 (WAhemisphere 33003 (North . 1) (South . 2))))))) | ||||
|     (assert (= 33004 (tagdict-get-by-name  ext-dict 'WAupper_left_lat))) | ||||
|     (assert (eq? 'WAupper_left_lat | ||||
| 	      (tagdict-get-by-num ext-dict 33004))) | ||||
|     (assert (eq? 'TIFFTAG:PHOTOMETRIC (tagdict-get-by-num ext-dict 262))) | ||||
|     (assert (eq? #f | ||||
| 	      (tagdict-tagval-get-by-num ext-dict 'WAupper_left_lat 0))) | ||||
|     (assert (= 1 | ||||
| 	      (tagdict-tagval-get-by-name ext-dict 'WAhemisphere 'North))) | ||||
|     (assert (eq? 'South | ||||
| 	      (tagdict-tagval-get-by-num ext-dict 'WAhemisphere 2)))) | ||||
| ) | ||||
| 
 | ||||
| (define (test-dictionary-consistency tiff-dict) | ||||
|   (cerr nl "Verifying the consistency of dictionary operations ..." nl) | ||||
|   (assert (tiff-directory? tiff-dict)) | ||||
|   (assert (positive? (tiff-directory-size tiff-dict)) | ||||
|           (not (tiff-directory-empty? tiff-dict))) | ||||
|   (assert (= | ||||
| 	    (tiff-directory-get tiff-dict 'TIFFTAG:IMAGEWIDTH) | ||||
| 	    (tiff-directory-get tiff-dict 256))) | ||||
|   (assert (eq? #f (tiff-directory-get tiff-dict 65500))) | ||||
|   (let ((not-given (list 'not-given))) | ||||
|     (assert (eq? not-given (tiff-directory-get tiff-dict 65500 | ||||
| 			     (lambda () not-given))))) | ||||
|   (let ((size (tiff-directory-size tiff-dict))) | ||||
|     (call-with-values | ||||
|       (lambda () | ||||
| 	(tiff-directory-fold-left tiff-dict | ||||
| 	  (lambda (el count) (values #t (+ 1 count))) 0)) | ||||
|       (lambda (size-via-fold) | ||||
| 	(assert (= size size-via-fold))))) | ||||
|   (let*-values | ||||
|     (((len) (tiff-directory-get tiff-dict 'TIFFTAG:IMAGELENGTH)) | ||||
|      ((len-via-fold prev-count) | ||||
|        (tiff-directory-fold-left tiff-dict | ||||
| 	 (lambda (dir-entry found prev-count) | ||||
| 	   (if (= (tiff-dir-entry-tag dir-entry) 257) | ||||
| 	     (values #f (force (tiff-dir-entry-value dir-entry)) | ||||
| 	       prev-count)		; and terminate now | ||||
| 	     (values #t #f (+ 1 prev-count)))) | ||||
| 	 #f 0))) | ||||
|     (assert (= len len-via-fold) | ||||
|       (< 0 prev-count (tiff-directory-size tiff-dict)))) | ||||
| ) | ||||
| 
 | ||||
| (define (test-known-values-from-dict tiff-dict) | ||||
|   (cerr nl "Getting sample data from the dictionary ") | ||||
|   (let | ||||
|     ((known-values | ||||
|        '((TIFFTAG:IMAGEWIDTH . 129) | ||||
| 	 (TIFFTAG:IMAGELENGTH . 122) | ||||
| 	 (TIFFTAG:BITSPERSAMPLE . 8) | ||||
| 	 (TIFFTAG:IMAGEDESCRIPTION . "JPEG:gnu-head-sm.jpg 129x122") | ||||
| 	 (TIFFTAG:COMPRESSION . 1) | ||||
| 	 (TIFFTAG:SAMPLESPERPIXEL . 1) | ||||
| 	 (TIFFTAG:STRIPBYTECOUNTS . 15738) ; the product of width and length | ||||
| 	 (TIFFTAG:XRESOLUTION . 72) | ||||
| 	 (TIFFTAG:CLEANFAXDATA . #f)))) | ||||
|     (for-each | ||||
|       (lambda (tag-val) | ||||
| 	(cerr "Tag " (car tag-val) "...") | ||||
| 	(let ((real (tiff-directory-get tiff-dict (car tag-val)))) | ||||
| 	  (cerr real nl) | ||||
| 	  (assert (equal? real (cdr tag-val))))) | ||||
|       known-values | ||||
|       )) | ||||
|   (assert (eq? 'NONE | ||||
| 	    (tiff-directory-get-as-symbol tiff-dict | ||||
| 	      'TIFFTAG:COMPRESSION))) | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| (define (test-reading-pixel-matrix tiff-dict eport) | ||||
|   (cerr nl "Reading the pixel matrix and spot-checking it ...") | ||||
|   ; Make sure we can handle this particular TIFF image | ||||
|   ; No compression | ||||
|   (assert (eq? 'NONE | ||||
| 	    (tiff-directory-get-as-symbol tiff-dict | ||||
| 	      'TIFFTAG:COMPRESSION))) | ||||
|   (assert (= 1 (tiff-directory-get tiff-dict 'TIFFTAG:SAMPLESPERPIXEL))) | ||||
|   (assert (= 8 (tiff-directory-get tiff-dict 'TIFFTAG:BITSPERSAMPLE))) | ||||
| 
 | ||||
|   (let* | ||||
|     ((ncols  (tiff-directory-get tiff-dict 'TIFFTAG:IMAGEWIDTH)) | ||||
|      (_      (assert (number? ncols) (positive? ncols))) | ||||
|      (nrows  (tiff-directory-get tiff-dict 'TIFFTAG:IMAGELENGTH)) | ||||
|      (_      (assert (number? nrows) (positive? nrows))) | ||||
|      (rows-per-strip (tiff-directory-get tiff-dict 'TIFFTAG:ROWSPERSTRIP | ||||
| 		       (lambda () nrows))) | ||||
|      (_      (assert (positive? rows-per-strip))) | ||||
|      (strip-offsets (tiff-directory-get tiff-dict 'TIFFTAG:STRIPOFFSETS | ||||
| 		      (lambda () (error "STRIPOFFSETS must be present!")))) | ||||
|       ; make it a u32vector | ||||
|      (strip-offsets | ||||
|        (cond | ||||
| 	 ((u32vector? strip-offsets) strip-offsets) | ||||
| 	 ((u16vector? strip-offsets) | ||||
| 	   (list->u32vector (u16vector->list strip-offsets))) | ||||
| 	 (else (u32vector strip-offsets)))) | ||||
|      (image-size (* nrows ncols)) | ||||
|      (strip-size (* rows-per-strip ncols)) | ||||
|      (image (make-u8vector image-size 0)) | ||||
|     ) | ||||
|     (cerr nl "Loading the image matrix of the size " image-size | ||||
|       " bytes...") | ||||
|     (let outer ((strip 0) (i 0)) | ||||
|       (if (>= strip (u32vector-length strip-offsets)) #f | ||||
| 	(let ((i-end (min (+ i strip-size) image-size))) | ||||
| 	  (endian-port-setpos eport (u32vector-ref strip-offsets strip)) | ||||
| 	  (let inner ((i i)) | ||||
| 	    (if  (>= i i-end) (outer (++ strip) i) | ||||
| 	      (begin | ||||
| 		(u8vector-set! image i (endian-port-read-int1 eport)) | ||||
| 		(inner (++ i)))))))) | ||||
|     (assert (= 255 (u8vector-ref image 0)) | ||||
|             (= 248 (u8vector-ref image 17))) | ||||
|     ;(display image) | ||||
|     )) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| (cerr nl "Reading the sample TIFF file " sample-tiff-file "..." nl) | ||||
| (let* ((eport (make-endian-port (open-input-file sample-tiff-file) #t)) | ||||
| 	(tiff-dict (read-tiff-file eport))) | ||||
|   (print-tiff-directory tiff-dict (current-output-port)) | ||||
|   (test-known-values-from-dict tiff-dict) | ||||
|   (test-dictionary-consistency tiff-dict) | ||||
|   (test-reading-pixel-matrix tiff-dict eport) | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| (cerr nl "All tests passed" nl) | ||||
		Loading…
	
		Reference in New Issue
	
	 Rolf-Thomas Happe
						Rolf-Thomas Happe