[cl-typesetting-devel] Vertical space at the top and bottom of a page
iso at wemba.edu.pl
iso at wemba.edu.pl
Fri Apr 13 02:58:48 UTC 2007
I seem to have a partial solution to the problem of
v-spacing appearing at the top of a page, but it's a
stab in the dark. I don't understand in detail how
compile-text, fit-lines, do-layout and spread-boxes
work, so I may have introduced a fatal bug.
The idea is to discard one v-spacing box at the front
of the list of boxes in a vbox and one at the rear if
it comes before (resp. after) a text-line with dy > 0,
hoping that it originated from a top or bottom margin
of an element at which the pagebreak was inserted (or
perhaps a vspace, which seems to have the same effect).
It's not a perfect solution, since some pages (or
columns) are shorter than they could be. The unwanted
v-spacing boxes should probably be removed before the
size of the box is measured, but I do not know how
to do this.
I have attached a complete example of a two-column
document (run the TEST-DOCUMENT function).
Of course, the problem of section headings appearing as
the last thing at the bottom of the page (or column)
remains unsolved. I wonder whether it would be possible
to make a simple fix by adding a parameter to
PARAGRAPH, called :forbidden-zone, which would specify
the height of a special area at the bottom of the page
(or vbox) where the paragraph must not start. Wouldn't
this be simpler to implement than a "keep-together"
option? This would not solve the problem of single
lines at the top, but at least section headings would
be kept together with the text.
-------------- next part --------------
(in-package :cl-user)
(eval-when (:compile-toplevel :load-toplevel :execute)
(asdf:oos 'asdf:load-op :cl-typesetting)
(use-package :tt))
(defun discard-unwanted-boxes (vbox)
"Discard the first v-spacing box with dy > 0, but only if it
comes before any text-line with dy > 0. Do the same thing at the
end of the list of boxes."
(when vbox
(setf (tt::boxes vbox)
(list-lobotomy #'discardable-box-p #'undiscardable-box-p
(tt::boxes vbox)))
vbox))
#+nil
(defun discard-unwanted-boxes (vbox)
vbox)
(defmethod undiscardable-box-p ((b tt::text-line))
(not (zerop (tt::dy b))))
(defmethod undiscardable-box-p ((b t))
nil)
(defmethod discardable-box-p ((b tt::v-spacing))
(not (zerop (tt::dy b))))
(defmethod discardable-box-p ((b t))
nil)
(defun list-lobotomy (delete-what-p stop-when-p list)
(list-tail-lobotomy delete-what-p stop-when-p
(list-head-lobotomy delete-what-p stop-when-p
list)))
(defun list-head-lobotomy (delete-what-p stop-when-p list)
"Delete elements satisfying DELETE-WHAT-P at the beginning of LIST
before the first element satisfying STOP-WHEN-P."
(if (funcall delete-what-p (car list))
(cdr list)
(do ((rest list (cdr rest)))
((or (null rest)
(funcall stop-when-p (car rest)))
list)
(let ((next (cadr rest)))
(when (and next (funcall delete-what-p next))
(rplacd rest (cddr rest)))))))
(defun list-tail-lobotomy (delete-what-p stop-when-p list)
"Delete first (counting from the end) element of LIST that satisfies
DELETE-WHAT-P if it comes after any element satisfying STOP-WHEN-P."
(let ((unwanted (position-if delete-what-p list :from-end t))
(stopper (position-if stop-when-p list :from-end t)))
(when (and stopper unwanted (< stopper unwanted))
(let ((l (nthcdr (1- unwanted) list)))
(rplacd l (cddr l))))
list))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defparameter *some-text* "Compact setting of text should be
avoided, except maybe for very small portions where a given area
of white space must be used in a certain way and it would not be
possible to fit the text into the area otherwise. Introduction of
extra white space between lines of text was traditionally done
through a process called leading. The typesetter inserted thin
strips of brass with a known thickness between lines of type
blocks in the printing frame to increase the distance between the
baselines of his types.")
(defun make-some-content ()
(compile-text ()
(dotimes (i 100)
(paragraph (:h-align :left
:top-margin 24
:bottom-margin 8
:font-size 14)
(format-string "Section ~d" (1+ i)))
(paragraph (:h-align :fill
:top-margin 0
:bottom-margin 4
:font-size 9)
*some-text* :hfill)
)
:vfill))
(defun test-document (&optional (filename #P"/tmp/output.pdf"))
(with-document ()
(let ((content (make-some-content)))
(loop
while (tt::boxes content)
do (draw-page-2-column
content
:width 482
:height 708
:margins '(42 48 49 48)
:header-height 3
:footer-height 3
)))
(write-document filename)))
(defun draw-page-2-column (content
&key width height margins
(header-height 12) (footer-height 12)
(column-gap 16)
)
(destructuring-bind (left &optional (top left) (right left) (bottom top)) margins
(let* ((x left)
(y (- height top))
(content-width (- width left right))
(content-height (- height top bottom))
(column-width (/ (- content-width column-gap) 2.0))
(main-height (- content-height header-height footer-height)))
(pdf:with-page (:bounds (vector 0 0 width height))
(pdf:with-saved-state
(tt::stroke (discard-unwanted-boxes
(make-filled-vbox content column-width main-height :top))
x (- y header-height))
(tt::stroke (discard-unwanted-boxes
(make-filled-vbox content column-width main-height :top))
(+ x column-width column-gap) (- y header-height))
(pdf::move-to x y)
(pdf::line-to (+ x content-width) y)
(pdf::move-to x bottom)
(pdf::line-to (+ x content-width) bottom)
(pdf::set-line-width .3)
(pdf::stroke)
)))))
More information about the cl-typesetting-devel
mailing list