1 ;;; Copyright (c) 2006 Zachary Beane, All Rights Reserved
3 ;;; Redistribution and use in source and binary forms, with or without
4 ;;; modification, are permitted provided that the following conditions
7 ;;; * Redistributions of source code must retain the above copyright
8 ;;; notice, this list of conditions and the following disclaimer.
10 ;;; * Redistributions in binary form must reproduce the above
11 ;;; copyright notice, this list of conditions and the following
12 ;;; disclaimer in the documentation and/or other materials
13 ;;; provided with the distribution.
15 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
16 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19 ;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 ;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21 ;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 ;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 ;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 ;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 ;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 ;;; Loading data from the 'glyf' table.
29 ;;; $Id: glyf.lisp,v 1.13 2006/03/23 22:22:01 xach Exp $
31 (in-package #:zpb-ttf
)
33 (defclass control-point
()
34 ((x :initarg
:x
:accessor x
)
35 (y :initarg
:y
:accessor y
)
36 (on-curve-p :initarg
:on-curve-p
:reader on-curve-p
)))
38 (defun make-control-point (x y on-curve-p
)
39 (make-instance 'control-point
42 :on-curve-p on-curve-p
))
44 (defmethod print-object ((control-point control-point
) stream
)
45 (print-unreadable-object (control-point stream
:type t
)
46 (format stream
"~D,~D~:[~;*~]"
47 (x control-point
) (y control-point
) (on-curve-p control-point
))))
49 (defmacro do-contour-segments
* ((p1 p2
) contour
&body body
)
50 (let ((length (gensym))
54 (next-point (gensym "NEXT-POINT"))
55 (midpoint (gensym "MIDPOINT"))
57 (loop (gensym "LOOP"))
58 (body-tag (gensym "BODY"))
59 (done-tag (gensym "DONE"))
64 (,length
(length ,contour
*))
65 ,stack
,next
,mid
,end
)
66 (unless (zerop ,length
)
67 (unless (on-curve-p (aref ,contour
* 0))
68 (setf ,stack
(aref ,contour
* 0)))
69 (flet ((,next-point
()
71 (prog1 (aref ,contour
* ,i
) (incf ,i
))))
73 (make-control-point (/ (+ (x p0
) (x p1
)) 2)
74 (/ (+ (y p0
) (y p1
)) 2)
82 ,end
(aref ,contour
* 0))
88 ,end
(,midpoint
,stack
,end
))
91 (if (on-curve-p ,next
)
97 ,end
(,midpoint
,stack
,next
)
108 (defun start-of-contour (contour)
109 "If first point of a contour is on the curve, return it, otherwise
110 find and return previous (possibly implicit) point on the curve."
111 (let ((first (aref contour
0)))
112 (if (on-curve-p first
)
114 (let ((last (aref contour
(1- (length contour
)))))
115 (if (on-curve-p last
)
117 ;; both are off curve, return the implicit on-curve point
118 (make-control-point (/ (+ (x first
) (x last
)) 2)
119 (/ (+ (y first
) (y last
)) 2)
122 (defmacro do-contour-segments
((p0 p1 p2
) contour
&body body
)
123 "A contour is made up of segments. A segment may be a straight line
124 or a curve. For each segment, bind the P0 and P2 variables to the
125 start and end points of the segment. If the segment is a curve, set P1
126 to the control point of the curve, otherwise set P1 to NIL."
127 ;; This macro started out life as a function and was converted.
129 (contour* (gensym "CONTOUR")))
130 `(let ((,contour
* ,contour
))
131 (when (plusp (length ,contour
*))
132 (let ((,start
(start-of-contour ,contour
*)))
133 (do-contour-segments* (,p1
,p2
)
136 (setf ,start
,p2
)))))))
138 (defun explicit-contour-points (contour)
139 (let ((new-contour (make-array (length contour
)
142 (when (and (plusp (length contour
))
143 (on-curve-p (aref contour
0)))
144 (vector-push-extend (aref contour
0) new-contour
))
145 (do-contour-segments* (p1 p2
)
148 (vector-push-extend p1 new-contour
))
149 (unless (eql p2
(aref contour
0))
150 (vector-push-extend p2 new-contour
)))
154 ;;; Locating a glyph's contours and bounding box in the font loader's
155 ;;; stream, and loading them
157 (defparameter *empty-contours
*
158 (make-array 0 :element-type
'(signed-byte 16)))
160 (defparameter *empty-bounding-box
*
163 :element-type
'(signed-byte 16)))
165 (defun empty-bounding-box ()
166 (copy-seq *empty-bounding-box
*))
168 (defun empty-contours ()
169 (copy-seq *empty-contours
*))
171 (defun dump-compound-flags (flags)
172 (format t
"XXX flags=~16,'0B~%" flags
)
173 (let ((meanings '((0 . ARG_1_AND_2_ARE_WORDS
)
174 (1 . ARGS_ARE_XY_VALUES
)
175 (2 . ROUND_XY_TO_GRID
)
176 (3 . WE_HAVE_A_SCALE
)
178 (5 . MORE_COMPONENTS
)
179 (6 . WE_HAVE_AN_X_AND_Y_SCALE
)
180 (7 . WE_HAVE_A_TWO_BY_TWO
)
181 (8 . WE_HAVE_INSTRUCTIONS
)
183 (10 . OVERLAP_COMPOUND
))))
184 (loop for
((bit . meaning
)) on meanings
185 do
(when (logbitp bit flags
)
186 (format t
"...~A~%" meaning
)))))
188 (defun transform-option-count (flags)
192 (cond ((logbitp scale-p flags
) 1)
193 ((logbitp xy-scale-p flags
) 2)
194 ((logbitp 2*2-scale-p flags
) 4)
197 (defun make-transformer (a b c d e f
)
198 "Given the elements of the transformation matrix specified by A, B,
199 C, D, E, and F, return a function of two arguments that returns the
200 arguments transformed as multiple values.
201 Ref: http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html"
202 (let ((m (max (abs a
) (abs b
)))
203 (n (max (abs c
) (abs d
))))
204 (when (<= (abs (- (abs a
) (abs b
))) 33/65536)
206 (when (<= (abs (- (abs c
) (abs d
))) 33/65536)
209 (values (* m
(+ (* (/ a m
) x
)
212 (* n
(+ (* (/ b n
) x
)
216 (defun transform-contours (fn contours
)
217 "Call FN with the X and Y coordinates of each point of each contour
218 in the vector CONTOURS. FN should return two values, which are used to
219 update the X and Y values of each point."
220 (loop for contour across contours do
221 (loop for p across contour do
222 (setf (values (x p
) (y p
))
223 (funcall fn
(x p
) (y p
))))))
225 (defun merge-contours (contours-list)
226 (let* ((total-contours (loop for contours in contours-list
227 summing
(length contours
)))
228 (merged (make-array total-contours
))
230 (dolist (contours contours-list merged
)
231 (loop for contour across contours do
232 (setf (aref merged i
) contour
)
235 (defvar *compound-contour-loop-check
*)
237 (defun read-compound-contours (loader)
238 (let ((contours-list '())
239 (stream (input-stream loader
)))
241 (let ((flags (read-uint16 stream
))
242 (font-index (read-uint16 stream
)))
243 (let ((position (file-position stream
))
244 (contours (read-contours-at-index font-index loader
)))
245 (push contours contours-list
)
246 (file-position stream position
)
247 (let ((args-words-p (logbitp 0 flags
))
248 (args-xy-values-p (logbitp 1 flags
))
249 (more-components-p (logbitp 5 flags
))
251 (cond ((and args-words-p args-xy-values-p
)
252 (setf arg1
(read-int16 stream
)
253 arg2
(read-int16 stream
)))
255 (setf arg1
(read-uint16 stream
)
256 arg2
(read-uint16 stream
))
257 (error "Compound glyphs relative to indexes not yet supported"))
259 (setf arg1
(read-int8 stream
)
260 arg2
(read-int8 stream
)))
262 (setf arg1
(read-uint8 stream
)
263 arg2
(read-uint8 stream
))
264 (error "Compound glyphs relative to indexes not yet supported")))
265 ;; Transform according to the transformation matrix
266 (let ((a 1.0) (b 0.0) (c 0.0) (d 1.0)
268 (ecase (transform-option-count flags
)
271 (setf a
(setf d
(read-fixed2.14 stream
))))
273 (setf a
(read-fixed2.14 stream
)
274 d
(read-fixed2.14 stream
)))
276 (setf a
(read-fixed2.14 stream
)
277 b
(read-fixed2.14 stream
)
278 c
(read-fixed2.14 stream
)
279 d
(read-fixed2.14 stream
))))
280 (let ((transform-fn (make-transformer a b c d e f
)))
281 (transform-contours transform-fn contours
)))
282 (unless more-components-p
283 (return (merge-contours contours-list
)))))))))
285 (defun read-points-vector (stream flags count axis
)
286 (let ((points (make-array count
:fill-pointer
0))
287 (short-index (if (eql axis
:x
) 1 2))
288 (same-index (if (eql axis
:x
) 4 5)))
289 (flet ((save-point (point)
290 (vector-push point points
)))
291 (loop for flag across flags
292 for short-p
= (logbitp short-index flag
)
293 for same-p
= (logbitp same-index flag
)
295 (let ((new-point (read-uint8 stream
)))
296 (save-point (if same-p new-point
(- new-point
)))))
300 (save-point (read-int16 stream
)))))))
303 (defun read-simple-contours (contour-count stream
)
304 "With the stream positioned immediately after the glyph bounding
305 box, read the contours data from STREAM and return it as a vector."
306 (let ((contour-endpoint-indexes (make-array contour-count
)))
307 (loop for i below contour-count
308 for endpoint-index
= (read-uint16 stream
)
309 do
(setf (svref contour-endpoint-indexes i
) endpoint-index
))
311 (let ((n-points (1+ (svref contour-endpoint-indexes
312 (1- contour-count
))))
313 (instruction-length (read-uint16 stream
)))
314 (loop for i below instruction-length
315 do
(read-byte stream
))
317 (let ((flags (make-array n-points
)))
319 while
(< i n-points
) do
320 (let ((flag-byte (read-uint8 stream
)))
321 (setf (svref flags i
) flag-byte
)
323 (when (logbitp 3 flag-byte
)
324 (let ((n-repeats (read-uint8 stream
)))
325 (loop repeat n-repeats do
326 (setf (svref flags i
) flag-byte
)
328 (let ((x-points (read-points-vector stream flags n-points
:x
))
329 (y-points (read-points-vector stream flags n-points
:y
))
330 (control-points (make-array n-points
:fill-pointer
0))
331 (contours (make-array contour-count
)))
332 (loop for x-point across x-points
333 for y-point across y-points
334 for flag across flags
335 for x
= x-point then
(+ x x-point
)
336 for y
= y-point then
(+ y y-point
)
338 (vector-push-extend (make-control-point x y
341 (loop for start
= 0 then
(1+ end
)
342 for end across contour-endpoint-indexes
344 do
(setf (svref contours i
)
345 (subseq control-points start
(1+ end
))))
348 (defmacro with-compound-contour-loop
(() &body body
)
349 `(let ((*compound-contour-loop-check
*
350 (if (boundp '*compound-contour-loop-check
*)
351 *compound-contour-loop-check
*
355 (defun read-contours-at-index (index loader
)
356 "Read the contours at glyph index INDEX, discarding bounding box
358 (let ((stream (input-stream loader
)))
359 (file-position stream
(+ (table-position "glyf" loader
)
360 (glyph-location index loader
)))
361 (let ((contour-count (read-int16 stream
))
362 (xmin (read-int16 stream
))
363 (ymin (read-int16 stream
))
364 (xmax (read-int16 stream
))
365 (ymax (read-int16 stream
)))
366 (declare (ignore xmin ymin xmax ymax
))
367 (if (= contour-count -
1)
368 (with-compound-contour-loop ()
369 ;; some fonts have compound contours that contain
370 ;; themselves, so we try to detect that.
371 (when (gethash index
*compound-contour-loop-check
*)
372 (return-from read-contours-at-index
373 (gethash index
*compound-contour-loop-check
*)))
374 ;; store a value for when we detect a loop
375 (setf (gethash index
*compound-contour-loop-check
*)
377 ;; It is reasonable for a particular contour to be
378 ;; included multiple times within the tree of compounds,
379 ;; though, so for that case we save the value and reuse
381 (setf (gethash index
*compound-contour-loop-check
*)
382 (read-compound-contours loader
)))
383 (read-simple-contours contour-count stream
)))))