;**************************************************************************** ; ; 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))) )))