;;; -*- Mode:LISP; Package:USER; Base:10; Readtable:CL -*-

(defvar *loaded-fonts*)
(defvar *dvi-stream*)

(defvar *h*)
(defvar *v*)
(defvar *w*)
(defvar *x*)
(defvar *y*)
(defvar *z*)
(defvar *f*)
(defvar *f-tfm*)

(defstruct (dvi-pos (:type :named-array))
  h v w x y z)

(defvar *dvi-pos-stack*)

(defun dvi-push-pos ()
  (let ((pos (make-dvi-pos)))
    (setf (dvi-pos-h pos) *h*)
    (setf (dvi-pos-v pos) *v*)
    (setf (dvi-pos-w pos) *w*)
    (setf (dvi-pos-x pos) *x*)
    (setf (dvi-pos-y pos) *y*)
    (setf (dvi-pos-z pos) *z*)
    (push pos *dvi-pos-stack*)))

(defun dvi-pop-pos ()
  (let ((pos (pop *dvi-pos-stack*)))
    (setq *h* (dvi-pos-h pos))
    (setq *v* (dvi-pos-v pos))
    (setq *w* (dvi-pos-w pos))
    (setq *x* (dvi-pos-x pos))
    (setq *y* (dvi-pos-y pos))
    (setq *z* (dvi-pos-z pos))))

(defun dvi-stream-position ()
  (cond ((eq *dvi-stream* 'si:null-stream) 0)
	(t
	 (send *dvi-stream* :read-pointer))))

(defun dvi-put-8b (val)
  (setq val (round val))
  (send *dvi-stream* :tyo (ldb (byte 8 0) val)))

(defun dvi-put-16b (val)
  (setq val (round val))
  (send *dvi-stream* :tyo (ldb (byte 8 8) val))
  (send *dvi-stream* :tyo (ldb (byte 8 0) val)))

(defun dvi-put-24b (val)
  (setq val (round val))
  (send *dvi-stream* :tyo (ldb (byte 8 16.) val))
  (send *dvi-stream* :tyo (ldb (byte 8 8) val))
  (send *dvi-stream* :tyo (ldb (byte 8 0) val)))

(defun dvi-put-32b (val)
  (setq val (round val))
  (send *dvi-stream* :tyo (ldb (byte 8 24.) val))
  (send *dvi-stream* :tyo (ldb (byte 8 16.) val))
  (send *dvi-stream* :tyo (ldb (byte 8 8) val))
  (send *dvi-stream* :tyo (ldb (byte 8 0) val)))

(defun dvi-set-char (char)
  "Typeset CHAR, then move right by its width."
  (let ((int (char-int char)))
    (when (>= int 256.) (ferror nil "not supported"))
    (when (>= int 128.)
      (dvi-put-8b 128.))
    (dvi-put-8b (ldb (byte 7 0) int)))
  (incf *h* (dvi-char-width char)))

(defun dvi-set-rule (height width)
  "Typeset black box of HEIGHT, WIDTH, then move right by WIDTH."
  (dvi-put-8b 132.)
  (dvi-put-32b height)
  (dvi-put-32b width)
  (incf *h* width))

(defun dvi-put-char (char)
  "Typeset CHAR, but don't move."
  (put-integer 133. char))

(defun dvi-put-rule (height width)
  "Typeset black box of HEIGHT, WIDTH, but don't move."
  (dvi-put-8b 137.)
  (dvi-put-32b height)
  (dvi-put-32b width))

(defun dvi-nop ()
  (dvi-put-8b 138.))

(defvar *last-bop*)
(defvar *number-of-pages*)

;;;prev should be -1 first time, then file offset of previous bop
(defun dvi-bop (c0 c1 c2 c3 c4 c5 c6 c7 c8 c9)
  (setq *h* 0)
  (setq *v* 0)
  (setq *w* 0)
  (setq *x* 0)
  (setq *y* 0)
  (setq *z* 0)
  (setq *f* nil)
  (setq *dvi-pos-stack* nil)
  (let ((this-bop (dvi-stream-position)))
    (dvi-put-8b 139.)
    (dvi-put-32b c0)
    (dvi-put-32b c1)
    (dvi-put-32b c2)
    (dvi-put-32b c3)
    (dvi-put-32b c4)
    (dvi-put-32b c5)
    (dvi-put-32b c6)
    (dvi-put-32b c7)
    (dvi-put-32b c8)
    (dvi-put-32b c9)
    (dvi-put-32b *last-bop*)
    (setq *last-bop* this-bop))
  (incf *number-of-pages*)
  (dvi-push)
  )

(defun dvi-eop ()
  (dvi-pop)
  (when (not (null *dvi-pos-stack*))
    (ferror nil "pos stack not empty"))
  (dvi-put-8b 140.))
  
(defun dvi-push ()
  (dvi-push-pos)
  (dvi-put-8b 141.))

(defun dvi-pop ()
  (dvi-pop-pos)
  (dvi-put-8b 142.))

(defun put-integer (command-base val)
  (setq val (round val))
  (cond ((and (<= -128. val) (< val 128.))
	 (dvi-put-8b command-base)
	 (dvi-put-8b val))
	((and (<= -32768. val) (< val 32768.))
	 (dvi-put-8b (+ command-base 1))
	 (dvi-put-16b val))
	((and (<= (- (ash 1 23.)) val) (< val (ash 1 23.)))
	 (dvi-put-8b (+ command-base 2))
	 (dvi-put-24b val))
	((and (<= (- (ash 1 31.)) val) (< val (ash 1 31.)))
	 (dvi-put-8b (+ command-base 3))
	 (dvi-put-32b val))
	(t
	 (ferror nil "too big"))))

(defun dvi-right (distance &optional set-pos-register)
  (cond ((= distance *w*)
	 (dvi-put-8b 147.))
	((= distance *x*)
	 (dvi-put-8b 152.))
	(t
	 (put-integer (ecase set-pos-register
			(w (setq *w* distance) 148.)
			(x (setq *x* distance) 153.)
			(nil 143.))
		      distance)))
  (incf *h* distance))

(defun dvi-down (distance &optional set-pos-register)
  (cond ((= distance *y*)
	 (dvi-put-8b 161.))
	((= distance *z*)
	 (dvi-put-8b 166.))
	(t
	 (put-integer (ecase set-pos-register
			(y (setq *y* distance) 162.)
			(z (setq *z* distance) 167.)
			(nil 157.))
		      distance)))
  (incf *v* distance))

(defun dvi-font (font-number &aux font-info)
  (dolist (f *loaded-fonts*
	     (ferror nil "font ~d not found" font-number))
    (when (= (cadr f) font-number)
      (setq font-info f)
      (return nil)))
  (cond ((< font-number 64.)
	 (dvi-put-8b (+ 171. font-number)))
	(t
	 (put-integer 235. font-number)))
  (setq *f* font-number)
  (setq *f-tfm* (caddr font-info)))

(defun dvi-special (string)
  (put-integer 239. (string-length string))
  (dotimes (i (string-length string))
    (dvi-put-8b (aref string i))))

(defun dvi-define-font (font-number checksum scale-factor design-size font-directory font-name)
  (put-integer 243. font-number)
  (dvi-put-32b checksum)
  (dvi-put-32b scale-factor)
  (dvi-put-32b design-size)
  (when (>= (string-length font-directory) 256.)
    (ferror nil "font-directory is too long"))
  (dvi-put-8b (string-length font-directory))
  (when (>= (string-length font-name) 256.)
    (ferror nil "font-name is too long"))
  (dvi-put-8b (string-length font-name))
  (dotimes (i (string-length font-directory))
    (dvi-put-8b (aref font-directory i)))
  (dotimes (i (string-length font-name))
    (dvi-put-8b (aref font-name i))))

;beginning of preamble
(defun dvi-pre (comment)
  (dvi-put-8b 247.)
  (dvi-put-8b 2)				;dvi format version
  (dvi-put-32b 25400000.)			;size scale, same as TEX
  (dvi-put-32b 473628672.)
  (dvi-put-32b 1000.)				;magnification * 1000.
  (when (>= (string-length comment) 256.)
    (ferror nil "comment too long"))
  (dvi-put-8b (string-length comment))
  (dotimes (i (string-length comment))
    (dvi-put-8b (aref comment i))))

(defvar *post-position*)

;beginning of postamble
(defun dvi-post ()
  (setq *post-position* (dvi-stream-position))
  (dvi-put-8b 248.)
  (dvi-put-32b *last-bop*)
  (dvi-put-32b 25400000.)			;size scale, same as TEX
  (dvi-put-32b 473628672.)
  (dvi-put-32b 1000.)				;magnification * 1000.
  (dvi-put-32b 0)				;height of largest page
  (dvi-put-32b 0)				;width of largest page
  (dvi-put-16b 5)				;maximum stack depth
  (dvi-put-16b *number-of-pages*)
  ;;then put out font definitions again
  )

;end of postamble
(defun dvi-post-post ()
  (dvi-put-8b 249.)
  (dvi-put-32b *post-position*)
  (dvi-put-8b 2)
  (dotimes (i 4)
    (dvi-put-8b 223.))
  (do ()
      ((zerop (ldb (byte 2 0) (dvi-stream-position))))
    (dvi-put-8b 223.)))




(defun read-bigendian-8b-unsigned (stream)
  (send stream :tyi))

(defun read-bigendian-16b-unsigned (stream)
  (let* ((b1 (send stream :tyi))
	 (b0 (send stream :tyi)))
    (dpb b1 (byte 8 8) b0)))

(defun read-bigendian-24b-unsigned (stream)
  (let* ((b2 (send stream :tyi))
	 (b1 (send stream :tyi))
	 (b0 (send stream :tyi)))
    (dpb b2 (byte 8 16.)
	 (dpb b1 (byte 8 8) b0))))

(defun read-bigendian-32b-unsigned (stream)
  (let* ((b3 (send stream :tyi))
	 (b2 (send stream :tyi))
	 (b1 (send stream :tyi))
	 (b0 (send stream :tyi)))
    (dpb b3 (byte 8 24.)
	 (dpb b2 (byte 8 16.)
	      (dpb b1 (byte 8 8) b0)))))

(defstruct (tfm (:type :named-array))
  file-length					;in words
  header-length					;in words
  smallest-char
  largest-char
  width-table-length
  height-table-length
  depth-table-length
  italic-table-length
  lig-table-length
  kern-table-length
  extend-table-length
  param-table-length
  checksum
  design-size
  coding-scheme
  font-family
  seven-bit-safe-flag
  width
  height
  depth
  italic
  )

(defun fix-word-to-ratio (num)
  (let ((factor 1))
    (when (ldb-test (byte 1 31.) num)
      (setq factor -1)
      (setq num (logand #o37777777777 (- num))))
    (* factor (+ (ldb (byte 12. 20.) num)
		 (/ (ldb (byte 20. 0) num) (ash 1 20.))))))

(defun read-tfm-file (name)
  (let ((tfm (make-tfm)))
    (with-open-file (stream name
			    :direction :input
			    :byte-size 8
			    :raw t)
      
      (setf (tfm-file-length tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-header-length tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-smallest-char tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-largest-char tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-width-table-length tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-height-table-length tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-depth-table-length tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-italic-table-length tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-lig-table-length tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-kern-table-length tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-extend-table-length tfm) (read-bigendian-16b-unsigned stream))
      (setf (tfm-param-table-length tfm) (read-bigendian-16b-unsigned stream))
      
      (send stream :set-pointer 24.)
      (when (< (tfm-header-length tfm) 2)
	(ferror nil "bad tfm file"))
      (setf (tfm-checksum tfm) (read-bigendian-32b-unsigned stream))
      (setf (tfm-design-size tfm) (fix-word-to-ratio (read-bigendian-32b-unsigned stream)))
      (cond ((> (tfm-header-length tfm) 2)
	     (when (< (tfm-header-length tfm) 12.)
	       (ferror nil "bad tfm file"))
	     (let ((length (read-bigendian-8b-unsigned stream)))
	       (when (or (< length 0) (> length 39.))
		 (ferror nil "bad tfm file"))
	       (let ((string (make-string length)))
		 (dotimes (i 39.)
		   (let ((c (read-bigendian-8b-unsigned stream)))
		     (when (< i length)
		       (setf (aref string i) c))))
		 (setf (tfm-coding-scheme tfm) string)))))
      (cond ((> (tfm-header-length tfm) 12.)
	     (when (< (tfm-header-length tfm) 17.)
	       (ferror nil "bad tfm file"))
	     (let ((length (read-bigendian-8b-unsigned stream)))
	       (when (or (< length 0) (> length 19.))
		 (ferror nil "bad tfm file"))
	       (let ((string (make-string length)))
		 (dotimes (i 19.)
		   (let ((c (read-bigendian-8b-unsigned stream)))
		     (when (< i length)
		       (setf (aref string i) c))))
		 (setf (tfm-font-family tfm) string)))))
      (when (>= (tfm-header-length tfm) 17.)
	(setf (tfm-seven-bit-safe-flag tfm) (read-bigendian-8b-unsigned stream))
	(read-bigendian-8b-unsigned stream)
	(read-bigendian-8b-unsigned stream)
	(read-bigendian-8b-unsigned stream)	;face code - don't appear to be really in any tfm files
	)
      ;;other header words are ignored

      ;;char-info
      (let ((char-info (make-array (1+ (- (tfm-largest-char tfm) (tfm-smallest-char tfm)))))
	    (width (make-array (tfm-width-table-length tfm)))
	    (height (make-array (tfm-height-table-length tfm)))
	    (depth (make-array (tfm-depth-table-length tfm)))
	    (italic (make-array (tfm-italic-table-length tfm)))
	    (lig (make-array (tfm-lig-table-length tfm)))
	    (kern (make-array (tfm-kern-table-length tfm)))
	    (extend (make-array (tfm-extend-table-length tfm)))
	    (param (make-array (tfm-param-table-length tfm))))
	(send stream :set-pointer (+ 24. (* 4 (tfm-header-length tfm))))
	(dotimes (i (array-length char-info))
	  (setf (aref char-info i) (read-bigendian-32b-unsigned stream)))

	;;width
	(dotimes (i (array-length width))
	  (setf (aref width i) (read-bigendian-32b-unsigned stream)))

	;;height
	(dotimes (i (array-length height))
	  (setf (aref height i) (read-bigendian-32b-unsigned stream)))

	;;depth
	(dotimes (i (array-length depth))
	  (setf (aref depth i) (read-bigendian-32b-unsigned stream)))

	;;italic
	(dotimes (i (array-length italic))
	  (setf (aref italic i) (read-bigendian-32b-unsigned stream)))

	;;lig
	(dotimes (i (array-length lig))
	  (setf (aref lig i) (read-bigendian-32b-unsigned stream)))

	;;kern
	(dotimes (i (array-length kern))
	  (setf (aref kern i) (read-bigendian-32b-unsigned stream)))

	;;extend
	(dotimes (i (array-length extend))
	  (setf (aref extend i) (read-bigendian-32b-unsigned stream)))

	;;param
	(dotimes (i (array-length param))
	  (setf (aref param i) (read-bigendian-32b-unsigned stream)))

	(let ((n-chars (1+ (- (tfm-largest-char tfm) (tfm-smallest-char tfm)))))
	  (setf (tfm-width tfm) (make-array n-chars))
	  (setf (tfm-height tfm) (make-array n-chars))
	  (setf (tfm-depth tfm) (make-array n-chars))
	  (setf (tfm-italic tfm) (make-array n-chars)))
	(do ((char (tfm-smallest-char tfm) (1+ char))
	     (end (tfm-largest-char tfm)))
	    ((> char end))
	  (let* ((char-info (aref char-info char))
		 (width-index (ldb (byte 8 24.) char-info))
		 (height-index (ldb (byte 4 20.) char-info))
		 (depth-index (ldb (byte 4 16.) char-info))
		 (italic-index (ldb (byte 6 10.) char-info))
		 (tag (ldb (byte 2 8) char-info))
		 (remainder (ldb (byte 8 0) char-info)))
	    tag remainder
	    (setf (aref (tfm-width tfm) char) (fix-word-to-ratio (aref width width-index)))
	    (setf (aref (tfm-height tfm) char) (fix-word-to-ratio (aref height height-index)))
	    (setf (aref (tfm-depth tfm) char) (fix-word-to-ratio (aref depth depth-index)))
	    (setf (aref (tfm-italic tfm) char) (fix-word-to-ratio (aref italic italic-index)))
	    )))

      )
    tfm))
		  

(defconst *font-directory* "angel:/usr/lib/tex/fonts/")

(defun output-font-definition (font-info)
  (let ((name (car font-info))
	(number (cadr font-info))
	(tfm (caddr font-info)))
    (dvi-define-font number
		     (tfm-checksum tfm)
		     (* (^ 2 16.) 10.)
		     (* (^ 2 16.) (tfm-design-size tfm))
		     ""
		     name)))

(defun load-font (font-number name)
  (let ((tfm (read-tfm-file (send (fs:merge-pathname-defaults name *font-directory*)
				  :new-raw-type "tfm"))))
    (let ((font-info (list name font-number tfm)))
      (setq *loaded-fonts* (append *loaded-fonts* (list font-info)))
      (output-font-definition font-info))))
    
(defun dvi-char-width (char)
  (* (tfm-design-size *f-tfm*)
     (aref (tfm-width *f-tfm*) char)
     (^ 2 16.)))

(defun dvi-char-height (char)
  (* (tfm-design-size *f-tfm*)
     (aref (tfm-height *f-tfm*) char)
     (^ 2 16.)))

(defconst *page-width-in-points* (* 7 72))	;7 inches
(defconst *normal-page-width-in-points* (* (+ 6 1/2) 72.))
(defconst *page-height-in-points* (* 10 72))	;10 inches
(defconst *normal-page-height-in-points* (* (+ 9 1/2) 72))
(defconst *lines-per-inch* 6)
(defconst *points-per-line* (/ 72. 6))


(defun dvi-next-line ()
  (let ((v *v*))
    ;;reset to top of page
    (dvi-pop)
    (dvi-push)
    (dvi-down (+ v (* (^ 2 16.) (/ 72. *lines-per-inch*))) nil)))  

(defun dvi-beginning-of-line ()
  (let ((v *v*))
    ;;reset to top of page
    (dvi-pop)
    (dvi-push)
    (dvi-down v nil)))

(defun points->scaled-points (points)
  (* (^ 2 16.) points))

(defun scaled-points->points (scaled-points)
  (/ scaled-points (^ 2 16.)))

(defmacro with-dvi-environment ((output-file-name) &body body)
  `(let ((*last-bop* -1)
	 (*number-of-pages* 0)
	 (*loaded-fonts* nil)
	 (*f* nil)
	 (*f-tfm* nil)
	 (*dvi-pos-stack* nil)
	 *h* *v* *w* *x* *y* *z*)
     (with-open-stream (*dvi-stream*
			 (cond ((null ,output-file-name)
				'si:null-stream)
			       (t
				(open ,output-file-name
				      :direction :output
				      :byte-size 8
				      :raw t))))
       (dvi-pre "LISPM DVI")
       ,@body
       (dvi-post)
       (output-fonts-in-decending-order)
       (dvi-post-post))))
	 
(defun output-fonts-in-decending-order ()
  (dolist (font-info (sort *loaded-fonts*
			   #'(lambda (f1 f2)
			       (> (cadr f1) (cadr f2)))))
    (output-font-definition font-info)))

(defconst *page-width* (points->scaled-points *page-width-in-points*))
(defconst *page-height* (points->scaled-points *page-height-in-points*))

(defflavor dvi-output-stream
	 ((*dvi-stream* nil)
	  (output-buffer (make-array 1024.))
	  (*last-bop* -1)
	  (*number-of-pages* 0)
	  (*f* nil)
	  (*f-tfm* nil)
	  (*dvi-pos-stack* nil)
	  (*h* nil)
	  (*v* nil)
	  (*w* nil)
	  (*x* nil)
	  (*y* nil)
	  (*z* nil)
	  (*loaded-fonts* nil))
	 (si:buffered-output-stream)
  :settable-instance-variables
  (:special-instance-variables
    *last-bop* *number-of-pages* *f* *f-tfm* *dvi-pos-stack*
    *h* *v* *w* *x* *y* *z*
    *dvi-stream* *loaded-fonts*
    )
  )

(defmethod (dvi-output-stream :after :init) (ignore)
  (when (null *dvi-stream*)
    (ferror nil "must specify a *dvi-stream*"))
  (dvi-pre "LISPM DVI")
  (load-font 0 "amtt10")
  )

(defmethod (dvi-output-stream :after :close) (&optional abort-p)
  (when (null abort-p)
    (when *dvi-pos-stack*
      (dvi-eop))
    (dvi-post)
    (output-fonts-in-decending-order)
    (dvi-post-post))
  (send *dvi-stream* :close abort-p)
  )

(defmethod (dvi-output-stream :new-output-buffer) ()
  (values output-buffer 0 (array-length output-buffer)))

(defmethod (dvi-output-stream :send-output-buffer) (ignore end)
  (dotimes (i end)
    (let ((char (aref output-buffer i)))
      (when (null *dvi-pos-stack*)
	(dvi-bop *number-of-pages* 0 0 0 0 0 0 0 0 0)
	)
      (when (null *f*)
	(dvi-font 0))
      (when (or (> *h* *page-width*)
		(char-equal char #\return))
	(dvi-set-rule (points->scaled-points 10)
		      (points->scaled-points 5))
	(dvi-next-line))
      (when (> *v* *page-height*)
	(dvi-eop)
	(dvi-bop *number-of-pages* 0 0 0 0 0 0 0 0 0))
    
      (cond ((char-equal char #\return))
	    ((< char 128.)
	     (dvi-set-char char))
	    (t
	     (ferror nil "Unknown character ~@c" char))
	    ))))

(defmethod (dvi-output-stream :discard-output-buffer) (ignore)
  )

(defun open-dvi-file ()
  (make-instance 'dvi-output-stream
		 :*dvi-stream* (open "angel:/lmi/pace/mf/test.dvi"
				      :direction :output
				      :byte-size 8
				      :raw t)))

(defun test ()
  (with-open-stream (s (open-dvi-file))
    (send s :string-out "Hello")))
	       
(defun test1 ()
  (with-open-stream (s (open-dvi-file))
    (with-open-file (i "angel:/etc/passwd")
      (stream-copy-until-eof i s))))


;;;;;



(defun dvi-set-line (string)
  (when (string-search-char #\tab string)
    (setq string (with-output-to-string (s)
		   (do ((col 0)
			(i 0 (1+ i))
			(end (string-length string)))
		       ((= i end))
		     (cond ((char= (aref string i) #\tab)
			    (send s :tyo #\space)
			    (incf col)
			    (loop until (zerop (ldb (byte 3 0) col))
				  do (progn (send s :tyo #\space)
					    (incf col)))
			    )
			   (t
			    (send s :tyo (aref string i))
			    (incf col)))))))
  (let ((end (* *normal-page-width-in-points* (^ 2 16.)))
	(semi (string-search-char #\; string)))
    (labels ((put-char-and-check-for-end (c)
					 (cond ((char= c #\space)
						(dvi-right (dvi-char-width #\X) 'w))
					       (t
						(dvi-set-char c)))
					 (when (>= *h* end)
					   (dvi-put-rule (dvi-char-height #\X)
							 (dvi-char-width #\X))
					   (return-from dvi-set-line nil))))
      (dvi-font 0)
      (dotimes (i (or semi (string-length string)))
	(put-char-and-check-for-end (aref string i)))
      (when semi
	(dvi-font 1)
	(do ((i semi (1+ i)))
	    ((= i (string-length string)))
	  (put-char-and-check-for-end (aref string i)))))))


(defun dvi-test-2 ()
  (with-dvi-environment ("angel:/lmi/pace/dvi/dvi2.dvi")
    (load-font 0 "amtt10")
    (dvi-new-page-with-header-and-line
      (fs:parse-pathname "angel:/lmi3/pace/t/comp/top/top.t") 5)
    (dvi-set-line "This is a test.")
    (dvi-next-line)
    (dvi-set-line (make-string 100 :initial-element #\x))
    (dvi-eop)))

(defun dvi-new-page-with-header-and-line (page-info page-number)
  (dvi-bop page-number 0 0 0 0 0 0 0 0 0)
  (dvi-font 0)
  (dvi-set-line
    (cond ((stringp page-info)
	   (dvi-make-simple-header page-info page-number))
	  (t
	   (dvi-make-file-header (car page-info) (caddr page-info) page-number))))
  (dvi-pop)
  (dvi-push)
  (dvi-down (points->scaled-points (/ *points-per-line* 2)))
  (dvi-put-rule (points->scaled-points 3/4)
		(points->scaled-points *normal-page-width-in-points*))
  (dvi-down (points->scaled-points *points-per-line*))
  )

(defun dvi-make-simple-header (center-string page-number)
  (let ((n-chars (floor *normal-page-width-in-points*
			(scaled-points->points (dvi-char-width #\0))))
	(time (time:get-universal-time)))
    (let ((header (make-string n-chars :initial-element #\space)))
      (let ((start (- (floor n-chars 2) (floor (string-length center-string) 2))))
	(copy-array-portion center-string 0 (string-length center-string)
			    header start (+ start (string-length center-string))))
      (let ((page-string (format nil "Page ~d" page-number))
	    (date-string (time:print-universal-time time nil nil :|DD MMM YY|)))
	(cond ((evenp page-number)
	       (copy-array-portion page-string 0 (string-length page-string)
				   header 0 (string-length page-string))
	       (copy-array-portion date-string 0 (string-length date-string)
				   header (- n-chars (string-length date-string)) n-chars))
	      (t
	       (copy-array-portion date-string 0 (string-length date-string)
				   header 0 (string-length date-string))
	       (copy-array-portion page-string 0 (string-length page-string)
				   header (- n-chars (string-length page-string)) n-chars))))
      header)))


(defun dvi-make-file-header (pathname creation-date page-number)
  (let ((n-chars (floor *normal-page-width-in-points*
			(scaled-points->points (dvi-char-width #\0))))
	file-name
	)
    (let ((dir (send pathname :directory)))
      (when (consp dir)
	(setq dir (car (last dir))))
      (setq file-name (send (send pathname :new-directory (list :relative dir))
			    :string-for-host)))
    (let ((header (make-string n-chars :initial-element #\space)))
      (let ((start (- (floor n-chars 2) (floor (string-length file-name) 2))))
	(copy-array-portion file-name 0 (string-length file-name)
			    header start (+ start (string-length file-name))))
      (let ((page-string (format nil "Page ~d" page-number))
	    (date-string (time:print-universal-time creation-date nil nil :|DD MMM YY|)))
	(cond ((evenp page-number)
	       (copy-array-portion page-string 0 (string-length page-string)
				   header 0 (string-length page-string))
	       (copy-array-portion date-string 0 (string-length date-string)
				   header (- n-chars (string-length date-string)) n-chars))
	      (t
	       (copy-array-portion date-string 0 (string-length date-string)
				   header 0 (string-length date-string))
	       (copy-array-portion page-string 0 (string-length page-string)
				   header (- n-chars (string-length page-string)) n-chars))))
      header)))

(defun dvi-test ()
  (let ((*last-bop* -1)
	(*number-of-pages* 0)
	(*loaded-fonts* nil)
	(*f* nil)
	(*f-tfm* nil)
	)
    (with-open-file (*dvi-stream* "angel:/lmi/pace/dvi/dvi-test.dvi"
				  :direction :output
				  :byte-size 8
				  :raw t
				  )
      (dvi-pre "Testing")
      (load-font 0 "amtt10")
      (dvi-bop 1 0 0 0 0 0 0 0 0 0)
      (dvi-font 0)
      (dotimes (i 15.)
	(dvi-put-char (+ #\0 (mod i 10.)))
	(dvi-right (* 10. (dvi-char-width #\0)) nil))
      (dvi-next-line)
      (dotimes (i 150.)
	(dvi-set-char (+ #\0 (mod i 10.))))
      (dotimes (i 90.)
	(dvi-next-line)
	(dvi-put-char (+ #\0 (mod (+ i 2) 10.))))
      (dvi-eop)
      (dvi-post)
      (dolist (font-info *loaded-fonts*)
	(output-font-definition font-info))
      (dvi-post-post)
      )))

(defvar *page-list* nil)



(defvar *chars-per-line*)





(defvar *font-for-code* "amtt10")

(defun compute-chars-per-line ()
  (with-dvi-environment (nil)
    (load-font 0 *font-for-code*)
    (dvi-bop 1 0 0 0 0 0 0 0 0 0)
    (dvi-font 0)
    (return-from compute-chars-per-line
      (values (round (/ *normal-page-width-in-points*
			(scaled-points->points (dvi-char-width #\0))))))))




  

(defun dvi-test-3 ()
  (with-dvi-environment ("angel:/lmi/pace/dvi/dvi3.dvi")
    (load-font 0 "amtt10")
    (load-font 1 "amsltt10")
    (dvi-bop 1 0 0 0 0 0 0 0 0 0)
    (dvi-font 0)
    (dvi-set-line "foobar")
    (dvi-eop)
    ))





















(defun print-xpage-list (&optional page-filter-function)
  (with-dvi-environment ("angel:/lmi/pace/dvi/dvi2.dvi")
    (load-font 0 "amtt10")
    (load-font 1 "amsltt10")
    (do ((pages *xpage-list* (cdr pages))
	 (page-number 1 (1+ page-number)))
	((null pages))
      (cond ((null (car pages))
	     (dvi-bop page-number 0 0 0 0 0 0 0 0 0)
	     (dvi-font 0)
	     (dvi-down (points->scaled-points (* 4 72.)))
	     (dvi-right (points->scaled-points (/ *normal-page-width-in-points* 2)))
	     (dvi-set-line "@")
	     (dvi-eop))
	    ((or (null page-filter-function)
		 (funcall page-filter-function page-number))
	     (format t "[~d" page-number)
	     (dvi-new-page-with-header-and-line (car pages) page-number)
	     (do ((line (cadr (car pages)) (array-leader line 1)))
		 ((null line))
	       (dvi-set-line line)
	       (when (getf (array-leader line 2) :bold)
		 (dvi-beginning-of-line)
		 (dvi-right (points->scaled-points 1/2))
		 (dvi-set-line line))
	       (dvi-next-line))
	     (dvi-eop)
	     (format t "] "))))))
