1 ;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: TEXPARA; -*-
2 ;;; ---------------------------------------------------------------------------
3 ;;; Title: TeX like paragraph formatting
5 ;;; Author: Gilbert Baumann <gilbert@base-engineering.com>
6 ;;; License: MIT style (see below)
7 ;;; ---------------------------------------------------------------------------
8 ;;; (c) copyright 2005 by Gilbert Baumann
10 ;;; Permission is hereby granted, free of charge, to any person obtaining
11 ;;; a copy of this software and associated documentation files (the
12 ;;; "Software"), to deal in the Software without restriction, including
13 ;;; without limitation the rights to use, copy, modify, merge, publish,
14 ;;; distribute, sublicense, and/or sell copies of the Software, and to
15 ;;; permit persons to whom the Software is furnished to do so, subject to
16 ;;; the following conditions:
18 ;;; The above copyright notice and this permission notice shall be
19 ;;; included in all copies or substantial portions of the Software.
21 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 ;;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 ;;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 ;;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 ;;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 (defpackage :texpara
(:use
#:cl
))
35 (defparameter *line-penality
* 10)
36 (defparameter *hyphen-penality
* 50)
37 (defparameter *exhyphen-penality
* 50)
39 (defparameter *pre-tolerance
* 100)
40 (defparameter *tolerance
* 200)
42 (defparameter *tolerance
* 4000)
44 (defparameter *precision
* 1
45 "Resolution of your device.")
46 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
47 (defconstant +badness-infinite
+ 10000))
49 (eval-when (compile eval load
)
54 (defstruct (glue (:include node
))
62 (defstruct (box (:include node
))
66 (defstruct (discretionary (:include node
))
82 (defstruct break-point
85 cache
) ;from this to next
87 (defun break-points (boxen)
88 (let* ((res (cons (make-break-point :position -
1) nil
))
93 ((= (the fixnum i
) (the fixnum n
))
94 (setf (break-point-delta-width (car bpf
)) delta
)
96 (let ((box (svref boxen i
)))
97 (cond ((or (glue-p box
) (discretionary-p box
))
98 (setf (break-point-delta-width (car bpf
)) delta
99 (cdr bpf
) (cons (make-break-point :position i
) nil
)
103 (incf delta
(box-width box
))))))))
105 (defmacro map-split-points
(boxen bp cont
)
106 ;; bp is the break point just where the last break occurred
107 `(let ((i (break-point-position (car ,bp
)))
110 (nw 0) ;natural width
111 (w+ 0) ;stretchability
112 (w- 0)) ;shrinkability
113 ;; The break just before the current line might have been a \discretionary
114 ;; node; in case of that consider the post material
115 (cond ((and (>= i
0) (discretionary-p (svref ,boxen i
)))
118 (incf nw
(box-width box
)))
121 (discretionary-post (svref ,boxen i
)))))
122 ;; Now consider all other break points
124 (when (null (cdr ,bp
))
126 ;; go to next break point
127 (incf nw
(break-point-delta-width (car ,bp
)))
129 (setf i
(break-point-position (car ,bp
)))
130 (setf box
(svref ,boxen i
))
131 ;; pretend we would break here
133 (cond ((discretionary-p box
)
134 (mapc (lambda (box) (incf ddw
(box-width box
)))
135 (discretionary-pre box
))))
140 ;; now pretend we would not break here
141 (cond ((discretionary-p box
)
142 (decf nw ddw
) ;cancel effect of pre
145 (incf ddw
(box-width box
)))
147 (incf nw
(glue-width box
))
148 (incf w
+ (glue-stretch box
))
149 (incf w-
(glue-shrink box
)))
151 (error "Barf! no ~S in discretionary-no please." box
) )))
152 (discretionary-no box
))))
154 (incf nw
(glue-width box
))
155 (incf w
+ (glue-stretch box
))
156 (incf w-
(glue-shrink box
)))) )))
158 (defmacro map-feasible-split-points
(boxen bp width cont
)
160 (map-split-points ,boxen
,bp
161 (lambda (bp nw w
+ w-
)
162 (let ((badness (badness2 nw w
+ w-
,width
)))
163 (when (> badness
+badness-infinite
+)
164 ;; overful box already
166 (when (< badness
*tolerance
*)
167 (,cont bp badness
)))))))
169 (defun map-line (fun boxen start end
)
170 "Map function to all elements, which would make up the line between start
173 ;; forget leading glue
175 ((not (and (glue-p (elt boxen start
))
176 (< (1+ start
) end
))))
178 ;; forget dangling glue
179 ;; don't do that when at end of paragraph though
180 (unless (= end
(length boxen
))
182 ((not (and (glue-p (elt boxen
(1- end
)))
183 (< start
(1- end
)))))
186 (do ((i start
(+ i
1)))
188 (let ((box (elt boxen i
)))
189 (cond ((discretionary-p box
)
191 (mapc fun
(discretionary-post box
)))
193 (mapc fun
(discretionary-pre box
)))
195 (mapc fun
(discretionary-no box
)))))
196 ((funcall fun box
))))))
198 ;; when at end of boxen think yourself a \hfil glue
199 '(cond ((= end
(length boxen
))
200 (funcall fun
+hfil-glue
+)) ))
202 (defun badness2 (nw w
+ w- width
)
203 (let ((delta (- width nw
)))
212 (* 2 +badness-infinite
+))
214 (min +badness-infinite
+ (badness3 (- delta
) w-
)))))
219 (min +badness-infinite
+ (badness3 delta w
+))))) )))
221 (defun badness3 (a b
)
222 (floor (* a
(floor (* a
(floor (* 100 a
) b
)) b
)) b
))
224 (defun assign-glue (boxen width
)
230 (BOX (incf nw
(box-width k
)))
232 (incf nw
(glue-width k
))
233 (incf sha
(glue-shrink k
))
234 (incf sta
(glue-stretch k
)))))
235 (let ((delta (- width nw
)))
239 (setf (glue-assigned k
) 0))))
244 (multiple-value-bind (a lack
)
245 (round (if (zerop sha
)
247 (* delta
(/ (glue-shrink k
) sha
)))
249 (setf a
(* *precision
* a
))
251 (decf sha
(glue-shrink k
))
252 (setf (glue-assigned k
) a
)))))
257 (multiple-value-bind (a lack
)
258 (round (if (zerop sta
)
260 (* delta
(/ (glue-stretch k
) sta
)))
262 (setf a
(* *precision
* a
))
264 (decf sta
(glue-stretch k
))
265 (setf (glue-assigned k
) a
)))) )) )))
267 (defun make-white-space-glue (w)
274 (defun format-paragraph (boxen width
)
287 (setf boxen
(coerce boxen
'vector
))
288 (setf sps
(minimum-split boxen width
))
289 (when r2
::*debug-tex-p
*
290 (format *trace-output
* "==== sps = ~S.~%" sps
))
293 ;; ### don't know why this happens.
294 (setf sps
(list (length boxen
))))
296 (p1 (pop sps
) (pop sps
)))
298 (let ((ln (line-subseq boxen p0
(min (length boxen
) (+ p1
1)))))
299 (assign-glue ln width
)
303 (defun minimum-split (boxen width
)
305 (break-point-position (caar x
)))
306 (minimum-split* boxen
(break-points boxen
) width
)))
308 (defun minimum-split* (boxen bp width
)
310 (cond ((null (cdr bp
))
312 ((setf res
(break-point-cache (car bp
)));muessen wir noch modifizieren
316 (setf (break-point-cache (car bp
))
318 (minimum-split** boxen bp width
))))))))
320 (defun minimum-split** (line3 bp3 width3
)
324 ;;(princ "@") (finish-output)
328 (best-d #.
(expt +badness-infinite
+ 2)))
329 (map-feasible-split-points line3 bp3 width3
331 (multiple-value-bind (points demerit
)
332 (minimum-split* line3 bp width3
)
333 (setf demerit
(+ (expt (+ *line-penality
* badness
) 2)
335 (when (or (null best-d
) (< demerit best-d
))
340 (values (and best-bad
341 (cons (cons best-bp best-bad
) best-pts
))
344 (defun line-subseq (boxen start end
)
346 (map-line (lambda (x) (push x res
)) boxen start end
)