[cl-typesetting-devel] Reusing bits of code

Peter Seibel peter at gigamonkeys.com
Thu Jul 14 19:06:24 UTC 2005


On Jul 8, 2005, at 9:17 PM, Peter Seibel wrote:

>
> On Jul 8, 2005, at 9:10 AM, Marc Battyani wrote:
>
>
>> You can get the "natural" size on some text content with something  
>> like
>> that:
>>
>> (compute-boxes-natural-size
>>    (boxes (compile-text ()
>>           (with-style (:font "Times-Roman") "foo"))) #'dx)
>> 15.996
>>
>
> Okay, more puzzles. Why does this function:
>
> (defun measure-logo ()
>   (let* ((big-font 48)
>      (small-font 16)
>      (extra-spacing 7)
>      (content
>       (compile-text ()
>         (with-style (:font "Didot-Bold" :font-size big-font) (put- 
> string "gigamonkeys")) :eol
>         (vspace -2)
>         (hspace 69)
>         (with-style (:font "Optima-Regular" :font-size small-font)
>           (loop for char across "CONSULTING" do
>            (put-string (string char))
>            (unless (char= char #\G)
>              (typeset::add-box
>               (make-instance 'typeset::h-spacing
>                      :dx extra-spacing
>                      :max-expansion extra-spacing
>                      :max-compression extra-spacing
>                      :expansibility 1.0
>                      :compressibility 1.0))))))))
>     (values
>      (typeset::compute-boxes-natural-size (boxes content)  
> #'typeset::dx)
>      (typeset::compute-boxes-natural-size (boxes content)  
> #'typeset::dy))))
>
> return
>
> 521.12006
> 823.6001
>
> The logo is *not* bigger than a piece of 8.5x11 paper!

So, I figured this one out. Sort of. The problem is that compute- 
boxes-natural-size simply sums the values returned by the size-fn (dx  
or dy here). Which means that if you have CHAR-BOX, CHAR-BOX, :EOL,  
CHAR-BOX, CHAR-BOX the "natural" width is the sum of the widths of  
the four char-boxes even though there's a line break. Similarly, the  
height is even more off because it sums the heights of a bunch of  
boxes that are actually all in a row. Here are two functions that  
seem to more or less do what I want though I'm not sure if I've dealt  
with all the edge cases:

(defun natural-width (content)
   (loop with max-width = 0
      with current-width = 0
      for box in (boxes content)
      for dx = (typeset::dx box)
      when (eql box :eol) do (setf current-width 0)
      do (incf current-width dx)
      do (setf max-width (max current-width max-width))
      finally (return max-width)))

(defun natural-height (content)
   (loop with max-height = 0
      with total-height = 0
      for box in (boxes content)
      for dy = (typeset::dy box)
      when (eql box :eol) do
        (incf total-height (* typeset::*leading-ratio* max-height))
        (setf max-height 0)
      do (setf max-height (max max-height dy))
      finally (return total-height)))

This lets me write a function like this to position a block of text  
in the middle of a page at its natural size:

(defun small-envelope (&optional (address *gigamonkeys*) (file "/tmp/ 
small-envelope.pdf"))
   (pdf:with-document ()
     (pdf:with-page (:bounds (vector 0 0 *small-envelope-width*  
*small-envelope-height*))
       (let* ((content (compile-text ()
                (with-style (:font "Optima-Regular" :font-size 12)
              (paragraph ()
                (loop for line in address do
                 (put-string line)
                 (typeset::add-box :eol))))))
          (width (natural-width content))
          (height (natural-height content))
          (x (/ (- *small-envelope-width* width) 2))
          (y (- *small-envelope-height* (/ (- *small-envelope-height*  
height) 2))))
      (draw-block content x y width height)))
     (pdf:write-document file)))

If there's an easier way to do this, I'd be glad to hear it.

-Peter

-- 
Peter Seibel           * peter at gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/





More information about the cl-typesetting-devel mailing list