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 ;;; An object for working with glyphs from the font. Some fields are
28 ;;; lazily loaded from the input-stream of the font-loader when needed.
30 ;;; $Id: glyph.lisp,v 1.28 2007/08/08 16:21:19 xach Exp $
32 (in-package #:zpb-ttf
)
38 :documentation
"The font-loader from which this glyph originates.")
42 :documentation
"The index of this glyph within the font file, used
43 to look up information in various structures in the truetype file.")
51 :initarg
:bounding-box
52 :accessor bounding-box
)))
54 (defmethod initialize-instance :after
((glyph glyph
)
55 &key code-point font-index font-loader
57 (flet ((argument-error (name)
58 (error "Missing required initarg ~S" name
)))
60 (argument-error :font-loader
))
61 (cond ((and code-point font-index
)) ;; do nothing
63 (setf (font-index glyph
)
64 (code-point-font-index code-point font-loader
)))
66 (let ((code-point (font-index-code-point font-index font-loader
)))
67 (when (zerop code-point
)
69 (or (postscript-name-code-point (postscript-name glyph
))
71 (setf (code-point glyph
) code-point
)))
73 (argument-error (list :font-index
:code-point
))))))
75 (defmethod print-object ((glyph glyph
) stream
)
76 (print-unreadable-object (glyph stream
:type t
:identity nil
)
77 ;; FIXME: Is this really going to be Unicode?
78 (format stream
"~S U+~4,'0X"
79 (postscript-name glyph
)
83 ;;; Glyph-specific values determined from data in the font-loader
85 (defgeneric left-side-bearing
(object)
86 (:method
((glyph glyph
))
87 (bounded-aref (left-side-bearings (font-loader glyph
))
90 (defmethod (setf left-side-bearing
) (new-value glyph
)
91 (setf (bounded-aref (left-side-bearings (font-loader glyph
))
97 (defgeneric kerning-offset
(left right loader
))
99 (defmethod kerning-offset ((left-glyph glyph
) (right-glyph glyph
)
100 (font-loader font-loader
))
101 (let ((kerning-table-key (logior (ash (font-index left-glyph
) 16)
102 (font-index right-glyph
))))
103 (gethash kerning-table-key
(kerning-table font-loader
) 0)))
105 (defmethod kerning-offset ((left character
) (right character
)
106 (font-loader font-loader
))
107 (kerning-offset (find-glyph left font-loader
)
108 (find-glyph right font-loader
)
111 (defmethod kerning-offset ((left null
) right font-loader
)
112 (declare (ignore left right font-loader
))
115 (defmethod kerning-offset (left (right null
) font-loader
)
116 (declare (ignore left right font-loader
))
119 (defgeneric advance-width
(object)
120 (:method
((glyph glyph
))
121 (bounded-aref (advance-widths (font-loader glyph
))
122 (font-index glyph
))))
124 (defmethod (setf advance-width
) (new-value (glyph glyph
))
125 (setf (bounded-aref (advance-widths (font-loader glyph
))
129 (defgeneric kerned-advance-width
(object next
)
130 (:method
((object glyph
) next
)
131 (+ (advance-width object
)
132 (kerning-offset object next
(font-loader object
)))))
134 (defgeneric location
(object)
135 (:method
((glyph glyph
))
136 (with-slots (font-index font-loader
)
138 (+ (table-position "glyf" font-loader
)
139 (glyph-location font-index font-loader
)))))
141 (defgeneric data-size
(object)
142 (:method
((glyph glyph
))
143 (with-slots (font-index font-loader
)
145 (- (glyph-location (1+ font-index
) font-loader
)
146 (glyph-location font-index font-loader
)))))
149 ;;; Initializing delayed data
151 (defmethod initialize-bounding-box ((glyph glyph
))
152 (if (zerop (data-size glyph
))
153 (setf (bounding-box glyph
) (empty-bounding-box))
154 (let ((stream (input-stream (font-loader glyph
))))
155 ;; skip contour-count
156 (file-position stream
(+ (location glyph
) 2))
157 (setf (bounding-box glyph
)
158 (vector (read-fword stream
)
161 (read-fword stream
))))))
163 (defmethod initialize-contours ((glyph glyph
))
164 (if (zerop (data-size glyph
))
165 (setf (contours glyph
) (empty-contours))
166 (let ((stream (input-stream (font-loader glyph
))))
167 (file-position stream
(location glyph
))
168 (let ((contour-count (read-int16 stream
)))
169 ;; skip glyph bounding box, 4 FWords
170 (advance-file-position stream
8)
171 (if (= contour-count -
1)
172 (setf (contours glyph
)
173 (read-compound-contours (font-loader glyph
)))
174 (setf (contours glyph
)
175 (read-simple-contours contour-count stream
)))))))
177 (defmethod bounding-box :before
((glyph glyph
))
178 (unless (slot-boundp glyph
'bounding-box
)
179 (initialize-bounding-box glyph
)))
181 (defmethod contours :before
((glyph glyph
))
182 (unless (slot-boundp glyph
'contours
)
183 (initialize-contours glyph
)))
185 (defgeneric contour-count
(object)
187 (length (contours object
))))
189 (defgeneric contour
(object idex
)
190 (:method
(object index
)
191 (aref (contours object
) index
)))
193 (defmacro do-contours
((contour object
&optional result
) &body body
)
196 `(let ((,obj
,object
))
197 (dotimes (,i
(contour-count ,obj
) ,result
)
198 (let ((,contour
(contour ,obj
,i
)))
201 (defgeneric right-side-bearing
(object)
202 (:method
((glyph glyph
))
203 (- (advance-width glyph
)
204 (- (+ (left-side-bearing glyph
) (xmax glyph
))
208 ;;; Producing a bounding box for a sequence of characters
210 (defgeneric string-bounding-box
(string loader
&key kerning
))
212 (defmethod string-bounding-box (string (font-loader font-loader
)
214 (cond ((zerop (length string
))
215 (empty-bounding-box))
216 ((= 1 (length string
))
217 (copy-seq (bounding-box (find-glyph (char string
0) font-loader
))))
220 (left (find-glyph (char string
0) font-loader
))
221 (xmin most-positive-fixnum
) (ymin most-positive-fixnum
)
222 (xmax most-negative-fixnum
) (ymax most-negative-fixnum
))
223 (flet ((update-bounds (glyph)
224 (setf xmin
(min (+ (xmin glyph
) origin
) xmin
)
225 xmax
(max (+ (xmax glyph
) origin
) xmax
)
226 ymin
(min (ymin glyph
) ymin
)
227 ymax
(max (ymax glyph
) ymax
))))
229 (loop for i from
1 below
(length string
)
230 for glyph
= (find-glyph (char string i
) font-loader
)
232 (incf origin
(advance-width left
))
234 (incf origin
(kerning-offset left glyph font-loader
)))
236 (update-bounds glyph
)))
237 (vector xmin ymin xmax ymax
)))))
240 ;;; Producing glyphs from loaders
242 (defgeneric glyph-exists-p
(character font-loader
)
243 (:method
((character glyph
) font-loader
)
244 (let ((index (font-index character
)))
245 (not (zerop index
))))
246 (:method
(character font-loader
)
247 (glyph-exists-p (find-glyph character font-loader
) font-loader
)))
249 (defgeneric find-glyph
(character font-loader
)
250 (:documentation
"Find the glyph object for CHARACTER in FONT-LOADER
251 and return it. If CHARACTER is an integer, treat it as a Unicode code
252 point. If CHARACTER is a Lisp character, treat its char-code as a
253 Unicode code point.")
254 (:method
((character integer
) (font-loader font-loader
))
255 (index-glyph (code-point-font-index character font-loader
) font-loader
))
256 (:method
((character character
) (font-loader font-loader
))
257 (find-glyph (char-code character
) font-loader
)))
259 (defgeneric index-glyph
(index font-loader
)
260 (:documentation
"Return the GLYPH object located at glyph index
261 INDEX in FONT-LOADER, or NIL if no glyph is defined for that
262 index. Despite the name, NOT the inverse of GLYPH-INDEX.")
263 (:method
(index font-loader
)
264 (let* ((cache (glyph-cache font-loader
))
265 (glyph (aref cache index
)))
268 (setf (aref cache index
)
269 (make-instance 'glyph
271 :font-loader font-loader
))))))
276 (defmethod postscript-name ((glyph glyph
))
277 (let* ((names (postscript-glyph-names (font-loader glyph
)))
278 (index (font-index glyph
))
279 (name (aref names index
)))
281 ((slot-boundp glyph
'code-point
)
282 (setf (aref names index
)
283 (format nil
"uni~4,'0X" (code-point glyph
))))