Umlaute raus!
[closure-html.git] / src / renderer / texpara.lisp
blobee5dd4399d5e2f298a1edac4fa9817c3ff42a146
1 ;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: TEXPARA; -*-
2 ;;; ---------------------------------------------------------------------------
3 ;;; Title: TeX like paragraph formatting
4 ;;; Created: ???
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:
17 ;;;
18 ;;; The above copyright notice and this permission notice shall be
19 ;;; included in all copies or substantial portions of the Software.
20 ;;;
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))
31 (in-package :texpara)
33 ;;;; Parameters
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)
51 (defstruct node
52 demerit)
54 (defstruct (glue (:include node))
55 width
56 shrink
57 shrink-unit
58 stretch
59 stretch-unit
60 assigned)
62 (defstruct (box (:include node))
63 width
64 data)
66 (defstruct (discretionary (:include node))
67 pre
68 post
69 no)
73 (defvar +hfil-glue+
74 ;; a \hfil fake
75 (make-glue :width 0
76 :shrink 0
77 :shrink-unit 0
78 :stretch 1e6
79 :stretch-unit 0))
82 (defstruct break-point
83 position
84 delta-width
85 cache) ;from this to next
87 (defun break-points (boxen)
88 (let* ((res (cons (make-break-point :position -1) nil))
89 (bpf res)
90 (n (length boxen))
91 (delta 0))
92 (do ((i 0 (+ i 1)))
93 ((= (the fixnum i) (the fixnum n))
94 (setf (break-point-delta-width (car bpf)) delta)
95 res)
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)
100 bpf (cdr bpf)
101 delta 0))
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)))
108 (box)
109 (ddw 0)
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)))
116 (mapc (lambda (box)
117 (cond ((box-p box)
118 (incf nw (box-width box)))
120 (error "Oops"))))
121 (discretionary-post (svref ,boxen i)))))
122 ;; Now consider all other break points
123 (loop
124 (when (null (cdr ,bp))
125 (return))
126 ;; go to next break point
127 (incf nw (break-point-delta-width (car ,bp)))
128 (setf ,bp (cdr ,bp))
129 (setf i (break-point-position (car ,bp)))
130 (setf box (svref ,boxen i))
131 ;; pretend we would break here
132 (setf ddw 0)
133 (cond ((discretionary-p box)
134 (mapc (lambda (box) (incf ddw (box-width box)))
135 (discretionary-pre box))))
136 (incf nw ddw)
138 (,cont ,bp nw w+ w-)
140 ;; now pretend we would not break here
141 (cond ((discretionary-p box)
142 (decf nw ddw) ;cancel effect of pre
143 (mapc (lambda (box)
144 (cond ((box-p box)
145 (incf ddw (box-width box)))
146 ((glue-p 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))))
153 (cond ((glue-p 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)
159 `(block raus
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
165 (return-from raus))
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
171 and end."
172 (let ((end end))
173 ;; forget leading glue
174 (do ()
175 ((not (and (glue-p (elt boxen start))
176 (< (1+ start) end))))
177 (incf start))
178 ;; forget dangling glue
179 ;; don't do that when at end of paragraph though
180 (unless (= end (length boxen))
181 (do ()
182 ((not (and (glue-p (elt boxen (1- end)))
183 (< start (1- end)))))
184 (decf end)))
185 ;; loop
186 (do ((i start (+ i 1)))
187 ((>= i end))
188 (let ((box (elt boxen i)))
189 (cond ((discretionary-p box)
190 (cond ((= i start)
191 (mapc fun (discretionary-post box)))
192 ((= i (1- end))
193 (mapc fun (discretionary-pre box)))
195 (mapc fun (discretionary-no box)))))
196 ((funcall fun box))))))
197 ;; Special case:
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)))
204 (cond ((= delta 0)
205 ;; perfect fit!
207 ((< delta 0)
208 (cond ((= w- 0)
209 +badness-infinite+)
210 ((> (- delta) w-)
211 ;; overful box
212 (* 2 +badness-infinite+))
214 (min +badness-infinite+ (badness3 (- delta) w-)))))
215 ((> delta 0)
216 (cond ((= w+ 0)
217 +badness-infinite+)
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)
225 (let ((nw 0)
226 (sha 0)
227 (sta 0))
228 (dolist (k boxen)
229 (etypecase k
230 (BOX (incf nw (box-width k)))
231 (GLUE
232 (incf nw (glue-width k))
233 (incf sha (glue-shrink k))
234 (incf sta (glue-stretch k)))))
235 (let ((delta (- width nw)))
236 (cond ((= delta 0)
237 (dolist (k boxen)
238 (and (glue-p k)
239 (setf (glue-assigned k) 0))))
240 ((< delta 0)
241 ;; shrink
242 (dolist (k boxen)
243 (and (glue-p k)
244 (multiple-value-bind (a lack)
245 (round (if (zerop sha)
246 (glue-width k)
247 (* delta (/ (glue-shrink k) sha)))
248 *precision*)
249 (setf a (* *precision* a))
250 (decf delta a)
251 (decf sha (glue-shrink k))
252 (setf (glue-assigned k) a)))))
253 ((> delta 0)
254 ;; shrink
255 (dolist (k boxen)
256 (and (glue-p k)
257 (multiple-value-bind (a lack)
258 (round (if (zerop sta)
259 (glue-width k)
260 (* delta (/ (glue-stretch k) sta)))
261 *precision*)
262 (setf a (* *precision* a))
263 (decf delta a)
264 (decf sta (glue-stretch k))
265 (setf (glue-assigned k) a)))) )) )))
267 (defun make-white-space-glue (w)
268 (make-glue :width w
269 :shrink (* 1/2 w)
270 :shrink-unit 0
271 :stretch (* 2/3 w)
272 :stretch-unit 0))
274 (defun format-paragraph (boxen width)
275 (let ((res nil))
276 (setq boxen
277 (append
278 boxen
279 (list
280 ;; a hfil fake
281 (make-glue :width 0
282 :shrink 0
283 :shrink-unit 0
284 :stretch 1e6
285 :stretch-unit 0) )))
286 (let ((sps ))
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))
291 #+NIL
292 (when (null sps)
293 ;; ### don't know why this happens.
294 (setf sps (list (length boxen))))
295 (do ((p0 0 p1)
296 (p1 (pop sps) (pop sps)))
297 ((null p1))
298 (let ((ln (line-subseq boxen p0 (min (length boxen) (+ p1 1)))))
299 (assign-glue ln width)
300 (push ln res)) ))
301 (reverse res)))
303 (defun minimum-split (boxen width)
304 (mapcar (lambda (x)
305 (break-point-position (caar x)))
306 (minimum-split* boxen (break-points boxen) width)))
308 (defun minimum-split* (boxen bp width)
309 (let (res)
310 (cond ((null (cdr bp))
311 (values nil 0))
312 ((setf res (break-point-cache (car bp)));muessen wir noch modifizieren
313 (values-list res))
315 (values-list
316 (setf (break-point-cache (car bp))
317 (multiple-value-list
318 (minimum-split** boxen bp width))))))))
320 (defun minimum-split** (line3 bp3 width3)
321 (cond ((null bp3)
322 (values nil 0))
324 ;;(princ "@") (finish-output)
325 (let ((best-bp nil)
326 (best-bad nil)
327 (best-pts nil)
328 (best-d #.(expt +badness-infinite+ 2)))
329 (map-feasible-split-points line3 bp3 width3
330 (lambda (bp badness)
331 (multiple-value-bind (points demerit)
332 (minimum-split* line3 bp width3)
333 (setf demerit (+ (expt (+ *line-penality* badness) 2)
334 demerit))
335 (when (or (null best-d) (< demerit best-d))
336 (setf best-d demerit
337 best-bp bp
338 best-bad badness
339 best-pts points)))))
340 (values (and best-bad
341 (cons (cons best-bp best-bad) best-pts))
342 best-d)))))
344 (defun line-subseq (boxen start end)
345 (let ((res nil))
346 (map-line (lambda (x) (push x res)) boxen start end)
347 (reverse res)))