initial commit

This commit is contained in:
Michael Filonenko 2012-07-29 11:16:52 +03:00
commit d2d095c30a
48 changed files with 3000 additions and 0 deletions

17
.gitignore vendored Normal file
View file

@ -0,0 +1,17 @@
*.fasl
*.aux
*.cp
*.fn
*.fns
*.info
*.ky
*.log
*.pg
*.toc
*.tp
*.tps
*.vr
*.rvs
*.vrs
include-stamp
web/

15
Makefile Normal file
View file

@ -0,0 +1,15 @@
.PHONY: web
web:
rm -rf web
mkdir web
make -C doc html pdf
cp doc/*.html doc/*.pdf web/
cp web/clx-truetype.html web/index.html
pages: web
git checkout gh-pages
cp web/* .
git commit -a -c master
rm -rf web/
git checkout -f master

5
README.txt Normal file
View file

@ -0,0 +1,5 @@
Pure Common Lisp TrueType antialiased fonts rendering using CLX and Xrender extension.
email: filonenko.mikhail@gmail.com
jabber: asvil@jabber.ru
skype: filonenko.mikhail

28
clx-truetype.asd Normal file
View file

@ -0,0 +1,28 @@
;;;; clx-truetype.asd
(asdf:defsystem #:clx-truetype
:serial t
:description "clx-truetype is pure common lisp solution for antialiased TrueType font rendering using CLX and XRender extension."
:author "Michael Filonenko <filonenko.mikhail@gmail.com>"
:license "MIT"
:depends-on (#:clx
#:zpb-ttf
#:cl-vectors
#:cl-paths-ttf
#:cl-aa
#:cl-fad
#:cl-store)
:components ((:file "package")
(:file "clx-utils")
(:file "font-cache")
(:file "clx-truetype")))
(asdf:defsystem #:clx-truetype-test
:serial t
:description "Testing library for clx-truetype."
:author "Michael Filonenko <filonenko.mikhail@gmail.com>"
:license "MIT"
:depends-on (#:clx-truetype)
:components ((:module test
:components ((:file "hello-world")))))

537
clx-truetype.lisp Normal file
View file

@ -0,0 +1,537 @@
;;;; clx-truetype.lisp
(in-package #:clx-truetype)
(defclass font ()
((family :type string :initarg :family :accessor font-family :documentation "Font family.")
(subfamily :type string :initarg :subfamily :accessor font-subfamily :documentation "Font subfamily. For e.g. regular, italic, bold, bold italib.")
(size :type numeric :initarg :size :accessor font-size :initform 12 :documentation "Font size in points.")
(underline :type boolean :initarg :underline :initform nil :accessor font-underline :documentation "Draw line under text string.")
(strikethrough :type boolean :initarg :strikethrough :initform nil :accessor font-strikethrough :documentation "Draw strike through text string.")
(overline :type boolean :initarg :overline :initform nil :accessor font-overline :documentation "Draw line over text string.")
(background :initarg :background :initform nil :accessor font-background :documentation "Background color.")
(foreground :initarg :foregroung :initform nil :accessor font-foregroung :documentation "Foreground color.")
(overwrite-gcontext :type boolean :initarg overwrite-gcontext :initform nil
:accessor font-overwrite-gcontext :documentation "Use font values for background and foreground colors.")
(antialiased :type boolean :initarg antialiased :initform t :accessor font-antialiased :documentation "Antialias text string."))
(:documentation "Class for representing font information."))
(defun check-valid-font-families (family subfamily)
(when (or (null (gethash family *font-cache*))
(null (gethash subfamily (gethash family *font-cache*))))
(error "Font is not found: ~A ~A" family subfamily)))
(defmethod initialize-instance :before
((instance font) &rest initargs &key family subfamily &allow-other-keys)
(check-valid-font-families family subfamily))
(defmethod (setf font-family) :before
(family (instance font))
(check-valid-font-families family (font-subfamily instance)))
(defmethod (setf font-subfamily) :before
(subfamily (instance font))
(check-valid-font-families (font-family instance) subfamily))
;;; ZPB-TTF font objects cache
(defun get-font-pathname (font)
(gethash (font-subfamily font) (gethash (font-family font) *font-cache*)))
(defvar *font-loader-cache* (make-hash-table :test 'equal))
(defmacro with-font-loader ((loader font) &body body)
(let ((exists-p (gensym))
(font-path (gensym)))
`(let ((,font-path (get-font-pathname ,font)))
(multiple-value-bind (,loader ,exists-p)
(gethash ,font-path *font-loader-cache* (zpb-ttf:open-font-loader ,font-path))
(unless ,exists-p
(setf (gethash ,font-path *font-loader-cache*) ,loader))
,@body))))
;;; Screen DPI
(defun screen-default-dpi (screen)
"Returns default dpi for @var{screen}. pixel width * 25.4/millimeters width"
(values (floor (* (xlib:screen-width screen) 25.4)
(xlib:screen-width-in-millimeters screen))
(floor (* (xlib:screen-height screen) 25.4)
(xlib:screen-height-in-millimeters screen))))
(defun screen-dpi (screen)
"Returns current dpi for @var{screen}."
(values (getf (xlib:screen-plist screen) :dpi-x
(floor (* (xlib:screen-width screen) 25.4)
(xlib:screen-width-in-millimeters screen)))
(getf (xlib:screen-plist screen) :dpi-y
(floor (* (xlib:screen-height screen) 25.4)
(xlib:screen-height-in-millimeters screen)))))
(defun (setf screen-dpi) (value screen)
"Sets current dpi for @var{screen}."
(setf (getf (xlib:screen-plist screen) :dpi-x) value
(getf (xlib:screen-plist screen) :dpi-y) value))
;;; Font metrics
(defun font-units->pixels-x (drawable font)
"px = funits*coeff. Function returns coeff."
(with-font-loader (loader font)
(multiple-value-bind (dpi-x dpi-y)
(screen-dpi (drawable-screen drawable))
(with-slots (size) font
(let* ((units/em (zpb-ttf:units/em loader))
(pixel-size-x (* size (/ dpi-x 72))))
(* pixel-size-x (/ units/em)))))))
(defun font-units->pixels-y (drawable font)
"px = funits*coeff. Function returns coeff."
(with-font-loader (loader font)
(multiple-value-bind (dpi-x dpi-y)
(screen-dpi (drawable-screen drawable))
(with-slots (size) font
(let* ((units/em (zpb-ttf:units/em loader))
(pixel-size-y (* size (/ dpi-y 72))))
(* pixel-size-y (/ units/em)))))))
(defun font-ascent (drawable font)
"Returns ascent of @var{font}. @{drawable} must be window, pixmap or screen."
(with-font-loader (loader font)
(ceiling (* (font-units->pixels-y drawable font) (zpb-ttf:ascender loader)))))
(defun font-descent (drawable font)
"Returns descent of @var{font}. @{drawable} must be window, pixmap or screen."
(with-font-loader (loader font)
(floor (* (font-units->pixels-y drawable font) (zpb-ttf:descender loader)))))
(defun font-line-gap (drawable font)
"Returns line gap of @var{font}. @{drawable} must be window, pixmap or screen."
(with-font-loader (loader font)
(ceiling (* (font-units->pixels-y drawable font) (zpb-ttf:line-gap loader)))))
;;; baseline-to-baseline = ascent - descent + line gap
(defun baseline-to-baseline (drawable font)
"Returns distance between baselines of @var{font}. @{drawable} must be window, pixmap or screen. ascent - descent + line gap"
(+ (font-ascent drawable font) (- (font-descent drawable font))
(font-line-gap drawable font)))
(defun text-bounding-box (drawable font string)
"Returns text bounding box. @{drawable} must be window, pixmap or screen. Text bounding box is only for contours. Bounding box for space (#x20) is zero."
(with-font-loader (loader font)
(let* ((bbox
(zpb-ttf:string-bounding-box string loader))
(units->pixels-x (font-units->pixels-x drawable font))
(units->pixels-y (font-units->pixels-y drawable font))
(xmin (zpb-ttf:xmin bbox))
(ymin (zpb-ttf:ymin bbox))
(xmax (zpb-ttf:xmax bbox))
(ymax (zpb-ttf:ymax bbox)))
(when (font-underline font)
(setf ymin (min ymin (- (zpb-ttf:underline-position loader)
(zpb-ttf:underline-thickness loader)))))
(when (font-overline font)
(setf ymax (max ymax (+ (zpb-ttf:ascender loader)
(zpb-ttf:underline-position loader)
(+ (zpb-ttf:underline-thickness loader))))))
(vector (floor (* xmin
units->pixels-x))
(floor (* ymin
units->pixels-y))
(ceiling (* xmax
units->pixels-x))
(ceiling (* ymax
units->pixels-y))))))
(defun text-width (drawable font string)
"Returns width of text bounding box. @{drawable} must be window, pixmap or screen."
(let ((bbox (text-bounding-box drawable font string)))
(- (xmax bbox) (xmin bbox))))
(defun text-height (drawable font string)
"Returns height of text bounding box. @{drawable} must be window, pixmap or screen."
(let ((bbox (text-bounding-box drawable font string)))
(- (ymax bbox) (ymin bbox))))
(defun text-line-bounding-box (drawable font string)
"Returns text line bounding box. @var{drawable} must be window, pixmap or screen. Text line bounding box is bigger than text bounding box. It's height is ascent + descent, width is sum of advance widths minus sum of kernings."
(with-font-loader (loader font)
(let* ((units->pixels-x (font-units->pixels-x drawable font))
(xmin 0)
(ymin (font-descent drawable font))
(ymax (font-ascent drawable font))
(string-length (length string))
(xmax (if (> string-length 0)
(zpb-ttf:advance-width (zpb-ttf:find-glyph (elt string 0) loader))
0)))
(if (zpb-ttf:fixed-pitch-p loader)
(setf xmax (* xmax string-length))
(do ((i 1 (1+ i)))
((>= i string-length))
(incf xmax
(+ (zpb-ttf:advance-width (zpb-ttf:find-glyph (elt string i) loader))
(zpb-ttf:kerning-offset (elt string (1- i)) (elt string i) loader)))))
(vector (floor (* xmin units->pixels-x))
ymin
(ceiling (* xmax
units->pixels-x))
ymax))))
(defun text-line-width (drawable font string)
"Returns width of text line bounding box. @var{drawable} must be window, pixmap or screen. It is sum of advance widths minus sum of kernings."
(let ((bbox (text-line-bounding-box drawable font string)))
(- (xmax bbox) (xmin bbox))))
(defun text-line-height (drawable font string)
"Returns height of text line bounding box. @var{drawable} must be window, pixmap or screen."
(let ((bbox (text-line-bounding-box drawable font string)))
(- (ymax bbox) (ymin bbox))))
(defun xmin (bounding-box)
"Returns left side x of @var{bounding-box}"
(typecase bounding-box
(vector (elt bounding-box 0))))
(defun ymin (bounding-box)
"Returns bottom side y of @var{bounding-box}"
(typecase bounding-box
(vector (elt bounding-box 1))))
(defun xmax (bounding-box)
"Returns right side x of @var{bounding-box}"
(typecase bounding-box
(vector (elt bounding-box 2))))
(defun ymax (bounding-box)
"Returns top side y of @var{bounding-box}"
(typecase bounding-box
(vector (elt bounding-box 3))))
;;; Font rendering
(defun clamp (value min max)
"Clamps the value 'value' into the range [min,max]."
(max min (min max value)))
(defun make-state (font)
"Wrapper around antialising and not antialiasing renderers."
(if (font-antialiased font)
(aa:make-state)
(aa-bin:make-state)))
(defun aa-bin/update-state (state paths)
"Update state for not antialiasing renderer."
(if (listp paths)
(dolist (path paths)
(aa-bin/update-state state path))
(let ((iterator (paths:path-iterator-segmented paths)))
(multiple-value-bind (i1 k1 e1) (paths:path-iterator-next iterator)
(declare (ignore i1))
(when (and k1 (not e1))
;; at least 2 knots
(let ((first-knot k1))
(loop
(multiple-value-bind (i2 k2 e2) (paths:path-iterator-next iterator)
(declare (ignore i2))
(aa-bin:line-f state
(paths:point-x k1) (paths:point-y k1)
(paths:point-x k2) (paths:point-y k2))
(setf k1 k2)
(when e2
(return))))
(aa-bin:line-f state
(paths:point-x k1) (paths:point-y k1)
(paths:point-x first-knot) (paths:point-y first-knot)))))))
state)
(defun update-state (font state paths)
"Wrapper around antialising and not antialiasing renderers."
(if (font-antialiased font)
(vectors:update-state state paths)
(aa-bin/update-state state paths)))
(defun cells-sweep (font state function &optional function-span)
"Wrapper around antialising and not antialiasig renderers."
(if (font-antialiased font)
(aa:cells-sweep state function function-span)
(aa-bin:cells-sweep state function function-span)))
(defun text-pixarray (drawable font string)
"Render a text string of 'face', returning a 2D (unsigned-byte 8) array
suitable as an alpha mask, and dimensions. This function returns five
values: alpha mask byte array, x-origin, y-origin (subtracted from
position before rendering), horizontal and vertical advances.
@var{drawable} must be window or pixmap."
(with-font-loader (font-loader font)
(let* ((bbox (text-bounding-box drawable font string))
(min-x (xmin bbox))
(min-y (ymin bbox))
(max-x (xmax bbox))
(max-y (ymax bbox))
(width (- max-x min-x))
(height (- max-y min-y))
(units->pixels-x (font-units->pixels-x drawable font))
(units->pixels-y (font-units->pixels-y drawable font))
(array (make-array (list height width)
:initial-element 0
:element-type '(unsigned-byte 8)))
(state (make-state font))
(paths (paths-ttf:paths-from-string font-loader string
:offset (paths:make-point (- min-x)
max-y)
:scale-x units->pixels-x
:scale-y (- units->pixels-y))))
(when (or (= 0 width) (= 0 height))
(return-from text-pixarray (values nil 0 0 0 0)))
(when (font-underline font)
(let* ((thickness (* units->pixels-y (zpb-ttf:underline-thickness font-loader)))
(underline-offset (* units->pixels-y (zpb-ttf:underline-position font-loader)))
(underline-path (paths:make-rectangle-path 0 (+ max-y (- underline-offset))
max-x (+ max-y (- underline-offset) thickness))))
(push underline-path paths)))
(when (font-strikethrough font)
(let* ((thickness (* units->pixels-y (zpb-ttf:underline-thickness font-loader)))
(underline-offset (* 2 units->pixels-y (zpb-ttf:underline-position font-loader)))
(line-path (paths:make-rectangle-path 0 (+ max-y underline-offset) max-x (+ max-y underline-offset thickness))))
(push line-path paths)))
(when (font-overline font)
(let* ((thickness (* units->pixels-y (zpb-ttf:underline-thickness font-loader)))
(underline-offset (* units->pixels-y (zpb-ttf:underline-position font-loader)))
(ascend (* units->pixels-y (zpb-ttf:ascender font-loader)))
(overline-path (paths:make-rectangle-path 0 (- max-y ascend underline-offset)
max-x
(- max-y ascend underline-offset thickness))))
(push overline-path paths)))
(update-state font state paths)
(cells-sweep font state
(lambda (x y alpha)
(when (and (<= 0 x (1- width))
(<= 0 y (1- height)))
(setf alpha (min 255 (abs alpha))
(aref array y x) (clamp
(floor (+ (* (- 256 alpha) (aref array y x))
(* alpha 255))
256)
0 255)))))
(values array
min-x
max-y
width
height))))
(defun text-line-pixarray (drawable font string)
"Render a text line of 'face', returning a 2D (unsigned-byte 8) array
suitable as an alpha mask, and dimensions. This function returns five
values: alpha mask byte array, x-origin, y-origin (subtracted from
position before rendering), horizontal and vertical advances.
@var{drawable} must be window or pixmap."
(with-font-loader (font-loader font)
(let* ((bbox (text-line-bounding-box drawable font string))
(min-x (xmin bbox))
(min-y (ymin bbox))
(max-x (xmax bbox))
(max-y (ymax bbox))
(width (- max-x min-x))
(height (- max-y min-y))
(units->pixels-x (font-units->pixels-x drawable font))
(units->pixels-y (font-units->pixels-y drawable font))
(array (make-array (list height width)
:initial-element 0
:element-type '(unsigned-byte 8)))
(state (make-state font))
(paths (paths-ttf:paths-from-string font-loader string
:offset (paths:make-point (- min-x)
max-y)
:scale-x units->pixels-x
:scale-y (- units->pixels-y))))
(when (or (= 0 width) (= 0 height))
(return-from text-line-pixarray (values nil 0 0 0 0)))
(when (font-underline font)
(let* ((thickness (* units->pixels-y (zpb-ttf:underline-thickness font-loader)))
(underline-offset (* units->pixels-y (zpb-ttf:underline-position font-loader)))
(underline-path (paths:make-rectangle-path 0 (+ max-y (- underline-offset))
max-x (+ max-y (- underline-offset) thickness))))
(push underline-path paths)))
(when (font-strikethrough font)
(let* ((thickness (* units->pixels-y (zpb-ttf:underline-thickness font-loader)))
(underline-offset (* 2 units->pixels-y (zpb-ttf:underline-position font-loader)))
(line-path (paths:make-rectangle-path 0 (+ max-y underline-offset) max-x (+ max-y underline-offset thickness))))
(push line-path paths)))
(when (font-overline font)
(let* ((thickness (* units->pixels-y (zpb-ttf:underline-thickness font-loader)))
(underline-offset (* units->pixels-y (zpb-ttf:underline-position font-loader)))
(ascend (* units->pixels-y (zpb-ttf:ascender font-loader)))
(overline-path (paths:make-rectangle-path 0 (- max-y ascend underline-offset)
max-x
(- max-y ascend underline-offset thickness))))
(push overline-path paths)))
(update-state font state paths)
(cells-sweep font state
(lambda (x y alpha)
(when (and (<= 0 x (1- width))
(<= 0 y (1- height)))
(setf alpha (min 255 (abs alpha))
(aref array y x) (clamp
(floor (+ (* (- 256 alpha) (aref array y x))
(* alpha 255))
256)
0 255)))))
(values array
min-x
max-y
width
height))))
(defun update-foreground (drawable gcontext font)
"Lazy updates foreground for drawable. @var{drawable} must be window or pixmap."
(let ((pixmap (or (getf (xlib:drawable-plist drawable) :ttf-pen-surface)
(setf (getf (xlib:drawable-plist drawable) :ttf-pen-surface)
(xlib:create-pixmap
:drawable drawable
:depth (xlib:drawable-depth drawable)
:width 1 :height 1)))))
(let ((color (the xlib:card32
(if (font-overwrite-gcontext font)
(font-foregroung font)
(xlib:gcontext-foreground gcontext)))))
(when (or (null (getf (xlib:drawable-plist drawable) :ttf-foreground))
(/= (getf (xlib:drawable-plist drawable) :ttf-foreground)
color))
(let ((previous-color (xlib:gcontext-foreground gcontext)))
(setf (xlib:gcontext-foreground gcontext) color)
(xlib:draw-point pixmap gcontext 0 0)
(setf (xlib:gcontext-foreground gcontext) previous-color)
(setf (getf (xlib:drawable-plist drawable) :ttf-foreground) color))))))
(defun update-background (drawable gcontext font x y width height)
"Lazy updates background for drawable. @var{drawable} must be window or pixmap."
(let ((previous-color (xlib:gcontext-foreground gcontext))
(color (the xlib:card32
(if (font-overwrite-gcontext font)
(font-background font)
(xlib:gcontext-background gcontext)))))
(setf (xlib:gcontext-foreground gcontext) color)
(xlib:draw-rectangle drawable gcontext x y width height t)
(setf (xlib:gcontext-foreground gcontext) previous-color)))
;;; Caching X11 objects
(defun get-drawable-picture (drawable)
(or (getf (xlib:drawable-plist drawable) :ttf-surface)
(setf (getf (xlib:drawable-plist drawable) :ttf-surface)
(xlib:render-create-picture drawable :format
(xlib:find-window-picture-format drawable)))))
(defun get-drawable-pen-picture (drawable)
(or (getf (xlib:drawable-plist drawable) :ttf-pen)
(setf (getf (xlib:drawable-plist drawable) :ttf-pen)
(xlib:render-create-picture
(or (getf (xlib:drawable-plist drawable) :ttf-pen-surface)
(setf (getf (xlib:drawable-plist drawable) :ttf-pen-surface)
(xlib:create-pixmap
:drawable drawable
:depth (xlib:drawable-depth drawable)
:width 1 :height 1)))
:format (xlib:find-window-picture-format drawable)))))
(defun display-alpha-picture-format (display)
(or (getf (xlib:display-plist display) :ttf-alpha-format)
(setf (getf (xlib:display-plist display) :ttf-alpha-format)
(first
(xlib:find-matching-picture-formats
display
:depth 8 :alpha 8 :red 0 :blue 0 :green 0)))))
;;; Drawing text
(defun draw-text (drawable gcontext font string x y &key (start 0) (end (length string)))
"Draws text string using @var{font} on @var{drawable} with graphic context @var{gcontext}. @var{x}, @var{y} are the left point of base line. @var{start} and @var{end} are used for substring rendering.
If @var{gcontext} has background color, text bounding box will be filled with it. Text line bounding box is bigger than text bounding box. @var{drawable} must be window or pixmap."
(when (>= start end)
(return-from draw-text))
(multiple-value-bind (alpha-data min-x max-y width height)
(text-pixarray drawable font (subseq string start end))
(when (or (= 0 width) (= 0 height))
(return-from draw-text))
(let* ((display (xlib:drawable-display drawable))
(image (xlib:create-image :width width :height height :depth 8 :data alpha-data))
(alpha-pixmap (xlib:create-pixmap :width width :height height :depth 8 :drawable drawable))
(alpha-gc (xlib:create-gcontext :drawable alpha-pixmap))
(alpha-picture
(progn
(xlib:put-image alpha-pixmap alpha-gc image :x 0 :y 0)
(xlib:render-create-picture alpha-pixmap :format
(display-alpha-picture-format display))))
(source-picture (get-drawable-pen-picture drawable))
(destination-picture (get-drawable-picture drawable)))
(update-foreground drawable gcontext font)
(update-background drawable gcontext font (+ x min-x) (- y max-y) width height)
;; Sync the destination picture with the gcontext
(setf (xlib:picture-clip-x-origin destination-picture) (xlib:gcontext-clip-x gcontext))
(setf (xlib:picture-clip-y-origin destination-picture) (xlib:gcontext-clip-y gcontext))
(setf (xlib:picture-subwindow-mode destination-picture) (xlib:gcontext-subwindow-mode gcontext))
(setf (xlib::picture-clip-mask destination-picture)
(xlib::gcontext-clip-mask gcontext))
(xlib:render-composite :over source-picture alpha-picture destination-picture 0 0 0 0 (+ x min-x) (- y max-y) width height)
nil)))
(defun draw-text-line (drawable gcontext font string x y &key (start 0) (end (length string)))
"Draws text string using @var{font} on @var{drawable} with graphic context @var{gcontext}. @var{x}, @var{y} are the left point of base line. @var{start} and @var{end} are used for substring rendering.
If @var{gcontext} has background color, text line bounding box will be filled with it. Text line bounding box is bigger than text bounding box. @var{drawable} must be window or pixmap."
(when (>= start end)
(return-from draw-text-line))
(multiple-value-bind (alpha-data min-x max-y width height)
(text-line-pixarray drawable font (subseq string start end))
(when (or (= 0 width) (= 0 height))
(return-from draw-text-line))
(let* ((display (xlib:drawable-display drawable))
(image (xlib:create-image :width width :height height :depth 8 :data alpha-data))
(alpha-pixmap (xlib:create-pixmap :width width :height height :depth 8 :drawable drawable))
(alpha-gc (xlib:create-gcontext :drawable alpha-pixmap))
(alpha-picture
(progn
(xlib:put-image alpha-pixmap alpha-gc image :x 0 :y 0)
(xlib:render-create-picture alpha-pixmap :format
(display-alpha-picture-format display))))
(source-picture (get-drawable-pen-picture drawable))
(destination-picture (get-drawable-picture drawable)))
(update-foreground drawable gcontext font)
(update-background drawable gcontext font (+ x min-x) (- y max-y) width height)
;; Sync the destination picture with the gcontext
(setf (xlib:picture-clip-x-origin destination-picture) (xlib:gcontext-clip-x gcontext))
(setf (xlib:picture-clip-y-origin destination-picture) (xlib:gcontext-clip-y gcontext))
(setf (xlib:picture-subwindow-mode destination-picture) (xlib:gcontext-subwindow-mode gcontext))
(setf (xlib::picture-clip-mask destination-picture)
(xlib::gcontext-clip-mask gcontext))
(xlib:render-composite :over source-picture alpha-picture destination-picture 0 0 0 0 (+ x min-x) (- y max-y) width height)
nil)))
;;; Test utils
(defun trgrey (i)
"Visualize alpha mask using graphic characters"
(cond
((> i 200) "██")
((> i 150) "▓▓")
((> i 100) "▒▒")
((> i 50) "░░")
(t " ")))
(defun print-pixarray (array)
"Print 2d array of alpha mask using graphic characters."
(do ((i 0 (1+ i)))
((>= i (array-dimension array 0)) nil)
(do ((j 0 (1+ j)))
((>= j (array-dimension array 1)) nil)
(format t "~A" (trgrey (aref array i j))))
(format t "~%")))
(defun font-lines-height (drawable font lines-count)
"Returns text lines height in pixels. For one line height is ascender+descender. For more than one line height is ascender+descender+linegap."
(if (> lines-count 0)
(+ (+ (xft:font-ascent drawable font)
(- (xft:font-descent drawable font)))
(* (1- lines-count) (+ (xft:font-ascent drawable font)
(- (xft:font-descent drawable font))
(xft:font-line-gap drawable font))))
0))
;;; "clx-truetype" goes here. Hacks and glory await!

12
clx-utils.lisp Normal file
View file

@ -0,0 +1,12 @@
;;; Utils
(in-package #:clx-truetype)
(defun drawable-screen (drawable)
(typecase drawable
(xlib:drawable
(dolist (screen (xlib:display-roots (xlib:drawable-display drawable)))
(when (equalp (xlib:screen-root screen) (xlib:drawable-root drawable))
(return screen))))
(xlib:screen drawable)
(t nil)))

34
doc/Makefile Normal file
View file

@ -0,0 +1,34 @@
.PHONY: clean
all: html pdf info
clean:
rm -rf include
rm -f *.pdf *.html *.info
rm -f *.aux *.cp *.fn *.fns *.ky *.log *.pg *.toc *.tp *.tps *.vr
rm -f include-stamp
clx-truetype.texinfo:
include-stamp: ../*.lisp ../*.asd
sbcl \
--eval '(let ((asdf:*central-registry* (cons #p"../" (cons #p"/home/michael/webspace/sb-texinfo/" asdf:*central-registry*)))) (require :sb-texinfo) (require :clx-truetype))' \
--eval '(sb-texinfo:generate-includes "include/" (list (find-package :clx-truetype)) :base-package :clx-truetype)' \
--eval '(quit)'
touch include-stamp
%.html: clx-truetype.texinfo style.css include-stamp
makeinfo --html --no-split --css-include=style.css $<
%.pdf: clx-truetype.texinfo include-stamp
texi2dvi -p $<
%.info: clx-truetype.texinfo include-stamp
makeinfo --no-split $<
html: clx-truetype.html
pdf: clx-truetype.pdf
info: clx-truetype.info

545
doc/clx-truetype.html Normal file
View file

@ -0,0 +1,545 @@
<html lang="en">
<head>
<title>CLX-TRUETYPE</title>
<meta http-equiv="Content-Type" content="text/html">
<meta name="description" content="CLX-TRUETYPE">
<meta name="generator" content="makeinfo 4.13">
<link title="Top" rel="top" href="#Top">
<link href="http://www.gnu.org/software/texinfo/" rel="generator-home" title="Texinfo Homepage">
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css"><!--
pre.display { font-family:inherit }
pre.format { font-family:inherit }
pre.smalldisplay { font-family:inherit; font-size:smaller }
pre.smallformat { font-family:inherit; font-size:smaller }
pre.smallexample { font-size:smaller }
pre.smalllisp { font-size:smaller }
span.sc { font-variant:small-caps }
span.roman { font-family:serif; font-weight:normal; }
span.sansserif { font-family:sans-serif; font-weight:normal; }
.node { visibility:hidden; height: 0px; }
.menu { visibility:hidden; height: 0px; }
.appendix { background-color:#d3d3d3; padding: 0.2em; }
.chapter { background-color:#d3d3d3; padding: 0.2em; }
.section { background-color:#d3d3d3; padding: 0.2em; }
.settitle { background-color:#d3d3d3; }
.contents { border: 2px solid black;
margin: 1cm 1cm 1cm 1cm;
padding-left: 3mm; }
.lisp { padding: 0; margin: 0em; }
body { padding: 2em 8em; font-family: sans-serif; }
h1 { padding: 1em; text-align: center; }
li { margin: 1em; }
--></style>
</head>
<body>
<h1 class="settitle">CLX-TRUETYPE</h1>
<div class="node">
<a name="Top"></a>
<p><hr>
Next:&nbsp;<a rel="next" accesskey="n" href="#Overview">Overview</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="#dir">(dir)</a>
</div>
<h2 class="unnumbered">Introduction</h2>
<p><span class="sc">clx-truetype </span> renders TrueType fonts over X11 drawable (window or pixmap)
using CLX, XRender, ZPB-TTF, CL-VECTORS.
<p><span class="sc">clx-truetype </span> was originally written for mcclim font rendering
by Gilbert Baumann and Andy Hefner.
<!-- Version control -->
<p><span class="sc">clx-truetype </span> is maintained in Git:
<pre class="example"> git clone git://github.com/filonenko-mikhail/clx-truetype
</pre>
<p>will get you a local copy.
<pre class="example"> <a href="http://github.com/filonenko-mikhail/clx-truetype/">http://github.com/filonenko-mikhail/clx-truetype/</a>
</pre>
<p>is the GitHub project page, where the issue tracker is located.
<div class="contents">
<h2>Table of Contents</h2>
<ul>
<li><a name="toc_Top" href="#Top">Introduction</a>
<li><a name="toc_Overview" href="#Overview">1 Overview</a>
<li><a name="toc_Examples" href="#Examples">2 Examples</a>
<li><a name="toc_Dictionary" href="#Dictionary">3 Dictionary</a>
<li><a name="toc_Concept-Index" href="#Concept-Index">Appendix A Concept Index</a>
<li><a name="toc_Function-Index" href="#Function-Index">Appendix B Function Index</a>
<li><a name="toc_Variable-Index" href="#Variable-Index">Appendix C Variable Index</a>
<li><a name="toc_Type-Index" href="#Type-Index">Appendix D Type Index</a>
<li><a name="toc_Colophon" href="#Colophon">Colophon</a>
</li></ul>
</div>
<ul class="menu">
<li><a accesskey="1" href="#Overview">Overview</a>
<li><a accesskey="2" href="#Examples">Examples</a>
<li><a accesskey="3" href="#Dictionary">Dictionary</a>
<li><a accesskey="4" href="#Concept-Index">Concept Index</a>
<li><a accesskey="5" href="#Function-Index">Function Index</a>
<li><a accesskey="6" href="#Variable-Index">Variable Index</a>
<li><a accesskey="7" href="#Type-Index">Type Index</a>
<li><a accesskey="8" href="#Colophon">Colophon</a>
</ul>
<div class="node">
<a name="Overview"></a>
<p><hr>
Next:&nbsp;<a rel="next" accesskey="n" href="#Examples">Examples</a>,
Previous:&nbsp;<a rel="previous" accesskey="p" href="#Top">Top</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
</div>
<!-- node-name, next, previous, up -->
<h2 class="chapter">1 Overview</h2>
<p><span class="sc">clx-truetype </span> is library for text rendering over X11 drawable using CLX, XRender,
ZPB-TTF, CL-VECTORS.
<p>TrueType font metrics
<div class="block-image"><img src="ttf-metrics.png" alt="ttf-metrics.png"></div>
<ul>
<li>TrueType hints are not supported.
<li>RGB antialiasing is not supported.
<li>Text rendering do not use XRender glyph sets.
</ul>
<div class="node">
<a name="Examples"></a>
<p><hr>
Next:&nbsp;<a rel="next" accesskey="n" href="#Dictionary">Dictionary</a>,
Previous:&nbsp;<a rel="previous" accesskey="p" href="#Overview">Overview</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
</div>
<!-- node-name, next, previous, up -->
<h2 class="chapter">2 Examples</h2>
<p>Drawing text is quite simple.
<p>First and only one time step is loading font cache using
<a href="#Function-cache_002dfonts"><code>cache-fonts</code></a>.
If you add font to your system, you should call it again.
<pre class="lisp"> (cache-fonts)
</pre>
<p>Make instance of font:
<pre class="lisp"> (font (make-instance 'font :family "Times New Roman" :subfamily "Bold Italic"
:size 12 :antialiased t))
</pre>
<p>Draw it using <a href="#Function-draw_002dtext"><code>draw-text</code></a> or <a href="#Function-draw_002dtext_002dline"><code>draw-text-line</code></a> functions:
<pre class="lisp"> (draw-text window grackon font "The quick brown fox jumps over the lazy dog." 100 100)
</pre>
<p>Move &lt;&lt;cursor&gt;&gt; using <a href="#Function-baseline_002dto_002dbaseline"><code>baseline-to-baseline</code></a> distance.
<p>Here it is complete example. Just insert it into repl, and evaluate (show-window).
<pre class="lisp"> (defpackage #:clx-truetype-test
(:nicknames :xft-test)
(:use #:cl #:xft)
(:export show-window))
(in-package :clx-truetype-test)
(defvar *display* (xlib:open-default-display))
(defvar *screen* (xlib:display-default-screen *display*))
(defvar *root* (xlib:screen-root *screen*))
(defun show-window ()
(let* ((black (xlib:screen-black-pixel *screen*))
(white (xlib:screen-white-pixel *screen*))
(window
(xlib:create-window :parent *root* :x 0 :y 0 :width 640 :height 480
:class :input-output
:background white
:event-mask '(:key-press :key-release :exposure :button-press
:structure-notify)))
(grackon (xlib:create-gcontext
:drawable window
:foreground black
:background white))
(font (make-instance 'font :family "Times New Roman" :subfamily "Bold Italic"
:size 12 :antialiased t)))
(unwind-protect
(progn
(xlib:map-window window)
(setf (xlib:gcontext-foreground grackon) black)
(xlib:event-case (*display* :force-output-p t
:discard-p t)
(:exposure ()
(draw-text window grackon font "The quick brown fox jumps over the lazy dog." 100 100)
(when (= 0 (random 2))
(rotatef (xlib:gcontext-foreground grackon) (xlib:gcontext-background grackon)))
(draw-text window grackon font "Съешь же ещё этих мягких французских булок, да выпей чаю." 100 (+ 100 (baseline-to-baseline window font)))
(setf (font-antialiased font) (= 0 (random 2)))
(if (= 0 (random 2))
(setf (font-subfamily font) "Regular")
(setf (font-subfamily font) "Italic"))
(draw-text window grackon font "Жебракують філософи при ґанку церкви в Гадячі, ще й шатро їхнє п’яне знаємо." 100 (+ 100 (* 2 (baseline-to-baseline window font))))
(draw-text window grackon font "Press space to exit. Нажмите пробел для выхода." 100 (+ 100 (* 3 (baseline-to-baseline window font)))))
(:button-press () t)
(:key-press (code state) (char= #\Space (xlib:keycode-&gt;character *display* code state)))))
(progn
(xlib:free-gcontext grackon)
(xlib:destroy-window window)
(xlib:display-force-output *display*)))))
</pre>
<p>Result is
<div class="block-image"><img src="example.png" alt="example.png"></div>
<div class="node">
<a name="Dictionary"></a>
<p><hr>
Next:&nbsp;<a rel="next" accesskey="n" href="#Concept-Index">Concept Index</a>,
Previous:&nbsp;<a rel="previous" accesskey="p" href="#Examples">Examples</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
</div>
<!-- node-name, next, previous, up -->
<h2 class="chapter">3 Dictionary</h2>
<p><a name="Package-xft"></a>
<div class="defun">
&mdash; Package: <b>xft</b><var><a name="index-xft-1"></a></var><br>
<blockquote><p>Package contains <code>api</code> for TrueType text rendering using <code>clx</code>, XRender. Glyphs information is obtained by <code>zpb-ttf</code>. Font rasterization is made by <code>cl-vectors</code>.
</p></blockquote></div>
<p><a name="index-g_t_0040earmuffs_007bfont_002ddirs_007d-2"></a><a name="Variable-_002afont_002ddirs_002a"></a>
<div class="defun">
&mdash; Variable: <b>*font-dirs*</b><var><a name="index-g_t_0040earmuffs_007bfont_002ddirs_007d-3"></a></var><br>
<blockquote><p>List of directories, which contain TrueType fonts.
</p></blockquote></div>
<p><a name="index-cache_002dfonts-4"></a><a name="Function-cache_002dfonts"></a>
<div class="defun">
&mdash; Function: <b>cache-fonts</b><var><a name="index-cache_002dfonts-5"></a></var><br>
<blockquote><p>Caches fonts from *font-dirs* directories.
</p></blockquote></div>
<p><a name="index-cache_002dfont_002dfile-6"></a><a name="Function-cache_002dfont_002dfile"></a>
<div class="defun">
&mdash; Function: <b>cache-font-file</b><var> pathname<a name="index-cache_002dfont_002dfile-7"></a></var><br>
<blockquote><p>Caches font file into hashmap.
</p></blockquote></div>
<p><a name="index-get_002dfont_002dfamilies-8"></a><a name="Function-get_002dfont_002dfamilies"></a>
<div class="defun">
&mdash; Function: <b>get-font-families</b><var><a name="index-get_002dfont_002dfamilies-9"></a></var><br>
<blockquote><p>Returns cached font families.
</p></blockquote></div>
<p><a name="index-get_002dfont_002dsubfamilies-10"></a><a name="Function-get_002dfont_002dsubfamilies"></a>
<div class="defun">
&mdash; Function: <b>get-font-subfamilies</b><var> font-family<a name="index-get_002dfont_002dsubfamilies-11"></a></var><br>
<blockquote><p>Returns font subfamilies for current. For e.g. regular, italic, bold, etc.
</p></blockquote></div>
<p><a name="index-font-12"></a><a name="Class-font"></a>
<div class="defun">
&mdash; Class: <b>font</b><var><a name="index-font-13"></a></var><br>
<blockquote><p>Class precedence list: <code>font, standard-object, t</code>
<p>Slots:
<ul>
<li><code>family</code> &mdash; initarg: <code>:family<!-- /@w --></code>; reader: <code>clx-truetype:font-family<!-- /@w --></code>; writer: <code>(setf&nbsp;clx-truetype:font-family)<!-- /@w --></code>
<p>Font family.
<li><code>subfamily</code> &mdash; initarg: <code>:subfamily<!-- /@w --></code>; reader: <code>clx-truetype:font-subfamily<!-- /@w --></code>; writer: <code>(setf&nbsp;clx-truetype:font-subfamily)<!-- /@w --></code>
<p>Font subfamily. For e.g. regular, italic, bold, bold italib.
<li><code>size</code> &mdash; initarg: <code>:size<!-- /@w --></code>; reader: <code>clx-truetype:font-size<!-- /@w --></code>; writer: <code>(setf&nbsp;clx-truetype:font-size)<!-- /@w --></code>
<p>Font size in points.
<li><code>underline</code> &mdash; initarg: <code>:underline<!-- /@w --></code>; reader: <code>clx-truetype:font-underline<!-- /@w --></code>; writer: <code>(setf&nbsp;clx-truetype:font-underline)<!-- /@w --></code>
<p>Draw line under text string.
<li><code>strikethrough</code> &mdash; initarg: <code>:strikethrough<!-- /@w --></code>; reader: <code>clx-truetype:font-strikethrough<!-- /@w --></code>; writer: <code>(setf&nbsp;clx-truetype:font-strikethrough)<!-- /@w --></code>
<p>Draw strike through text string.
<li><code>overline</code> &mdash; initarg: <code>:overline<!-- /@w --></code>; reader: <code>clx-truetype:font-overline<!-- /@w --></code>; writer: <code>(setf&nbsp;clx-truetype:font-overline)<!-- /@w --></code>
<p>Draw line over text string.
<li><code>background</code> &mdash; initarg: <code>:background<!-- /@w --></code>; reader: <code>clx-truetype:font-background<!-- /@w --></code>; writer: <code>(setf&nbsp;clx-truetype:font-background)<!-- /@w --></code>
<p>Background color.
<li><code>foreground</code> &mdash; initarg: <code>:foregroung<!-- /@w --></code>; reader: <code>clx-truetype:font-foregroung<!-- /@w --></code>; writer: <code>(setf&nbsp;clx-truetype:font-foregroung)<!-- /@w --></code>
<p>Foreground color.
<li><code>overwrite-gcontext</code> &mdash; initarg: <code>clx-truetype::overwrite-gcontext<!-- /@w --></code>; reader: <code>clx-truetype:font-overwrite-gcontext<!-- /@w --></code>; writer: <code>(setf&nbsp;clx-truetype:font-overwrite-gcontext)<!-- /@w --></code>
<p>Use font values for background and foreground colors.
<li><code>antialiased</code> &mdash; initarg: <code>clx-truetype::antialiased<!-- /@w --></code>; reader: <code>clx-truetype:font-antialiased<!-- /@w --></code>; writer: <code>(setf&nbsp;clx-truetype:font-antialiased)<!-- /@w --></code>
<p>Antialias text string.
</ul>
<p>Class for representing font information.
</p></blockquote></div>
<p><a name="index-screen_002ddefault_002ddpi-14"></a><a name="Function-screen_002ddefault_002ddpi"></a>
<div class="defun">
&mdash; Function: <b>screen-default-dpi</b><var> screen<a name="index-screen_002ddefault_002ddpi-15"></a></var><br>
<blockquote><p>Returns default dpi for @var{screen}. pixel width <code>*</code> 25.4/millimeters width
</p></blockquote></div>
<p><a name="index-screen_002ddpi-16"></a><a name="Function-screen_002ddpi"></a>
<div class="defun">
&mdash; Function: <b>screen-dpi</b><var> screen<a name="index-screen_002ddpi-17"></a></var><br>
<blockquote><p>Returns current dpi for @var{screen}.
</p></blockquote></div>
<p><a name="index-g_t_0040setf_007bscreen_002ddpi_007d-18"></a><a name="Function-_0028setf-screen_002ddpi_0029"></a>
<div class="defun">
&mdash; Function: <b>(setf screen-dpi)</b><var> value screen<a name="index-g_t_0040setf_007bscreen_002ddpi_007d-19"></a></var><br>
<blockquote><p>Sets current dpi for @var{screen}.
</p></blockquote></div>
<p><a name="index-font_002dascent-20"></a><a name="Function-font_002dascent"></a>
<div class="defun">
&mdash; Function: <b>font-ascent</b><var> drawable font<a name="index-font_002dascent-21"></a></var><br>
<blockquote><p>Returns ascent of @var{font}. @{drawable} must be window, pixmap or screen.
</p></blockquote></div>
<p><a name="index-font_002ddescent-22"></a><a name="Function-font_002ddescent"></a>
<div class="defun">
&mdash; Function: <b>font-descent</b><var> drawable font<a name="index-font_002ddescent-23"></a></var><br>
<blockquote><p>Returns descent of @var{font}. @{drawable} must be window, pixmap or screen.
</p></blockquote></div>
<p><a name="index-font_002dline_002dgap-24"></a><a name="Function-font_002dline_002dgap"></a>
<div class="defun">
&mdash; Function: <b>font-line-gap</b><var> drawable font<a name="index-font_002dline_002dgap-25"></a></var><br>
<blockquote><p>Returns line gap of @var{font}. @{drawable} must be window, pixmap or screen.
</p></blockquote></div>
<p><a name="index-baseline_002dto_002dbaseline-26"></a><a name="Function-baseline_002dto_002dbaseline"></a>
<div class="defun">
&mdash; Function: <b>baseline-to-baseline</b><var> drawable font<a name="index-baseline_002dto_002dbaseline-27"></a></var><br>
<blockquote><p>Returns distance between baselines of @var{font}. @{drawable} must be window, pixmap or screen. ascent <code>-</code> descent <code>+</code> line gap
</p></blockquote></div>
<p><a name="index-text_002dbounding_002dbox-28"></a><a name="Function-text_002dbounding_002dbox"></a>
<div class="defun">
&mdash; Function: <b>text-bounding-box</b><var> drawable font string<a name="index-text_002dbounding_002dbox-29"></a></var><br>
<blockquote><p>Returns text bounding box. @{drawable} must be window, pixmap or screen. Text bounding box is only for contours. Bounding box for space (#x20) is zero.
</p></blockquote></div>
<p><a name="index-text_002dwidth-30"></a><a name="Function-text_002dwidth"></a>
<div class="defun">
&mdash; Function: <b>text-width</b><var> drawable font string<a name="index-text_002dwidth-31"></a></var><br>
<blockquote><p>Returns width of text bounding box. @{drawable} must be window, pixmap or screen.
</p></blockquote></div>
<p><a name="index-text_002dheight-32"></a><a name="Function-text_002dheight"></a>
<div class="defun">
&mdash; Function: <b>text-height</b><var> drawable font string<a name="index-text_002dheight-33"></a></var><br>
<blockquote><p>Returns height of text bounding box. @{drawable} must be window, pixmap or screen.
</p></blockquote></div>
<p><a name="index-text_002dline_002dbounding_002dbox-34"></a><a name="Function-text_002dline_002dbounding_002dbox"></a>
<div class="defun">
&mdash; Function: <b>text-line-bounding-box</b><var> drawable font string<a name="index-text_002dline_002dbounding_002dbox-35"></a></var><br>
<blockquote><p>Returns text line bounding box. @var{drawable} must be window, pixmap or screen. Text line bounding box is bigger than text bounding box. It's height is ascent <code>+</code> descent, width is sum of advance widths minus sum of kernings.
</p></blockquote></div>
<p><a name="index-text_002dline_002dwidth-36"></a><a name="Function-text_002dline_002dwidth"></a>
<div class="defun">
&mdash; Function: <b>text-line-width</b><var> drawable font string<a name="index-text_002dline_002dwidth-37"></a></var><br>
<blockquote><p>Returns width of text line bounding box. @var{drawable} must be window, pixmap or screen. It is sum of advance widths minus sum of kernings.
</p></blockquote></div>
<p><a name="index-text_002dline_002dheight-38"></a><a name="Function-text_002dline_002dheight"></a>
<div class="defun">
&mdash; Function: <b>text-line-height</b><var> drawable font string<a name="index-text_002dline_002dheight-39"></a></var><br>
<blockquote><p>Returns height of text line bounding box. @var{drawable} must be window, pixmap or screen.
</p></blockquote></div>
<p><a name="index-xmin-40"></a><a name="Function-xmin"></a>
<div class="defun">
&mdash; Function: <b>xmin</b><var> bounding-box<a name="index-xmin-41"></a></var><br>
<blockquote><p>Returns left side x of @var{bounding-box}
</p></blockquote></div>
<p><a name="index-ymin-42"></a><a name="Function-ymin"></a>
<div class="defun">
&mdash; Function: <b>ymin</b><var> bounding-box<a name="index-ymin-43"></a></var><br>
<blockquote><p>Returns bottom side y of @var{bounding-box}
</p></blockquote></div>
<p><a name="index-xmax-44"></a><a name="Function-xmax"></a>
<div class="defun">
&mdash; Function: <b>xmax</b><var> bounding-box<a name="index-xmax-45"></a></var><br>
<blockquote><p>Returns right side x of @var{bounding-box}
</p></blockquote></div>
<p><a name="index-ymax-46"></a><a name="Function-ymax"></a>
<div class="defun">
&mdash; Function: <b>ymax</b><var> bounding-box<a name="index-ymax-47"></a></var><br>
<blockquote><p>Returns top side y of @var{bounding-box}
</p></blockquote></div>
<p><a name="index-draw_002dtext-48"></a><a name="Function-draw_002dtext"></a>
<div class="defun">
&mdash; Function: <b>draw-text</b><var> drawable gcontext font string x y &amp;key start end<a name="index-draw_002dtext-49"></a></var><br>
<blockquote><p>Draws text string using @var{font} on @var{drawable} with graphic context @var{gcontext}. @var{x}, @var{y} are the left point of base line. @var{start} and @var{end} are used for substring rendering.
If @var{gcontext} has background color, text bounding box will be filled with it. Text line bounding box is bigger than text bounding box. @var{drawable} must be window or pixmap.
</p></blockquote></div>
<p><a name="index-draw_002dtext_002dline-50"></a><a name="Function-draw_002dtext_002dline"></a>
<div class="defun">
&mdash; Function: <b>draw-text-line</b><var> drawable gcontext font string x y &amp;key start end<a name="index-draw_002dtext_002dline-51"></a></var><br>
<blockquote><p>Draws text string using @var{font} on @var{drawable} with graphic context @var{gcontext}. @var{x}, @var{y} are the left point of base line. @var{start} and @var{end} are used for substring rendering.
If @var{gcontext} has background color, text line bounding box will be filled with it. Text line bounding box is bigger than text bounding box. @var{drawable} must be window or pixmap.
</p></blockquote></div>
<p><a name="index-font_002dlines_002dheight-52"></a><a name="Function-font_002dlines_002dheight"></a>
<div class="defun">
&mdash; Function: <b>font-lines-height</b><var> drawable font lines-count<a name="index-font_002dlines_002dheight-53"></a></var><br>
<blockquote><p>Returns text lines height in pixels. For one line height is ascender+descender. For more than one line height is ascender+descender+linegap.
</p></blockquote></div>
<div class="node">
<a name="Concept-Index"></a>
<p><hr>
Next:&nbsp;<a rel="next" accesskey="n" href="#Function-Index">Function Index</a>,
Previous:&nbsp;<a rel="previous" accesskey="p" href="#Dictionary">Dictionary</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
</div>
<!-- node-name, next, previous, up -->
<h2 class="appendix">Appendix A Concept Index</h2>
<ul class="index-cp" compact>
</ul><div class="node">
<a name="Function-Index"></a>
<p><hr>
Next:&nbsp;<a rel="next" accesskey="n" href="#Variable-Index">Variable Index</a>,
Previous:&nbsp;<a rel="previous" accesskey="p" href="#Concept-Index">Concept Index</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
</div>
<!-- node-name, next, previous, up -->
<h2 class="appendix">Appendix B Function Index</h2>
<ul class="index-fn" compact>
<li><a href="#index-g_t_0040setf_007bscreen_002ddpi_007d-18"><code>(setf screen-dpi)</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-baseline_002dto_002dbaseline-26"><code>baseline-to-baseline</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-cache_002dfont_002dfile-6"><code>cache-font-file</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-cache_002dfonts-4"><code>cache-fonts</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-draw_002dtext-48"><code>draw-text</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-draw_002dtext_002dline-50"><code>draw-text-line</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-font_002dascent-20"><code>font-ascent</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-font_002ddescent-22"><code>font-descent</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-font_002dline_002dgap-24"><code>font-line-gap</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-font_002dlines_002dheight-52"><code>font-lines-height</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-get_002dfont_002dfamilies-8"><code>get-font-families</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-get_002dfont_002dsubfamilies-10"><code>get-font-subfamilies</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-screen_002ddefault_002ddpi-14"><code>screen-default-dpi</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-screen_002ddpi-16"><code>screen-dpi</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-text_002dbounding_002dbox-28"><code>text-bounding-box</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-text_002dheight-32"><code>text-height</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-text_002dline_002dbounding_002dbox-34"><code>text-line-bounding-box</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-text_002dline_002dheight-38"><code>text-line-height</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-text_002dline_002dwidth-36"><code>text-line-width</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-text_002dwidth-30"><code>text-width</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-xmax-44"><code>xmax</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-xmin-40"><code>xmin</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-ymax-46"><code>ymax</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-ymin-42"><code>ymin</code></a>: <a href="#Dictionary">Dictionary</a></li>
</ul><div class="node">
<a name="Variable-Index"></a>
<p><hr>
Next:&nbsp;<a rel="next" accesskey="n" href="#Type-Index">Type Index</a>,
Previous:&nbsp;<a rel="previous" accesskey="p" href="#Function-Index">Function Index</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
</div>
<!-- node-name, next, previous, up -->
<h2 class="appendix">Appendix C Variable Index</h2>
<ul class="index-vr" compact>
<li><a href="#index-g_t_0040earmuffs_007bfont_002ddirs_007d-2"><code>*font-dirs*</code></a>: <a href="#Dictionary">Dictionary</a></li>
<li><a href="#index-xft-1"><code>xft</code></a>: <a href="#Dictionary">Dictionary</a></li>
</ul><div class="node">
<a name="Type-Index"></a>
<p><hr>
Next:&nbsp;<a rel="next" accesskey="n" href="#Colophon">Colophon</a>,
Previous:&nbsp;<a rel="previous" accesskey="p" href="#Variable-Index">Variable Index</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
</div>
<!-- node-name, next, previous, up -->
<h2 class="appendix">Appendix D Type Index</h2>
<ul class="index-tp" compact>
<li><a href="#index-font-12"><code>font</code></a>: <a href="#Dictionary">Dictionary</a></li>
</ul><div class="node">
<a name="Colophon"></a>
<p><hr>
Previous:&nbsp;<a rel="previous" accesskey="p" href="#Type-Index">Type Index</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
</div>
<!-- node-name, next, previous, up -->
<h2 class="unnumbered">Colophon</h2>
<p>This manual is maintained in Texinfo, and automatically translated
into other forms (e.g. HTML or pdf). If you're <em>reading</em> this
manual in one of these non-Texinfo translated forms, that's fine, but
if you want to <em>modify</em> this manual, you are strongly advised to
seek out a Texinfo version and modify that instead of modifying a
translated version.
</body></html>

BIN
doc/clx-truetype.pdf Normal file

Binary file not shown.

207
doc/clx-truetype.texinfo Normal file
View file

@ -0,0 +1,207 @@
\input texinfo @c -*-texinfo-*-
@c %**start of header
@setfilename clx-truetype.info
@settitle CLX-TRUETYPE
@c %**end of header
@c for install-info
@dircategory Software development
@direntry
* clx-truetype: Documentation String to CLX TrueType renderer
@end direntry
@finalout
@setcontentsaftertitlepage
@macro project
@sc{clx-truetype }
@end macro
@titlepage
@title @project
@subtitle CLX TrueType Font Renderer
@end titlepage
@include include/sb-texinfo.texinfo
@node Top
@top Introduction
@project renders TrueType fonts over X11 drawable (window or pixmap)
using CLX, XRender, ZPB-TTF, CL-VECTORS.
@project was originally written for mcclim font rendering
by Gilbert Baumann and Andy Hefner.
@c Version control
@project is maintained in Git:
@example
git clone git://github.com/filonenko-mikhail/clx-truetype
@end example
will get you a local copy.
@example
@url{http://github.com/filonenko-mikhail/clx-truetype/}
@end example
is the GitHub project page, where the issue tracker is located.
@contents
@menu
* Overview::
* Examples::
* Dictionary::
* Concept Index::
* Function Index::
* Variable Index::
* Type Index::
* Colophon::
@end menu
@node Overview
@comment node-name, next, previous, up
@chapter Overview
@project is library for text rendering over X11 drawable using CLX, XRender,
ZPB-TTF, CL-VECTORS.
TrueType font metrics
@image{ttf-metrics}
@itemize
@item
TrueType hints are not supported.
@item
RGB antialiasing is not supported.
@item
Text rendering do not use XRender glyph sets.
@end itemize
@node Examples
@comment node-name, next, previous, up
@chapter Examples
Drawing text is quite simple.
First and only one time step is loading font cache using
@reffun{cache-fonts}.
If you add font to your system, you should call it again.
@lisp
(cache-fonts)
@end lisp
Make instance of font:
@lisp
(font (make-instance 'font :family "Times New Roman" :subfamily "Bold Italic"
:size 12 :antialiased t))
@end lisp
Draw it using @reffun{draw-text} or @reffun{draw-text-line} functions:
@lisp
(draw-text window grackon font "The quick brown fox jumps over the lazy dog." 100 100)
@end lisp
Move <<cursor>> using @reffun{baseline-to-baseline} distance.
Here it is complete example. Just insert it into repl, and evaluate (show-window).
@lisp
(defpackage #:clx-truetype-test
(:nicknames :xft-test)
(:use #:cl #:xft)
(:export show-window))
(in-package :clx-truetype-test)
(defvar *display* (xlib:open-default-display))
(defvar *screen* (xlib:display-default-screen *display*))
(defvar *root* (xlib:screen-root *screen*))
(defun show-window ()
(let* ((black (xlib:screen-black-pixel *screen*))
(white (xlib:screen-white-pixel *screen*))
(window
(xlib:create-window :parent *root* :x 0 :y 0 :width 640 :height 480
:class :input-output
:background white
:event-mask '(:key-press :key-release :exposure :button-press
:structure-notify)))
(grackon (xlib:create-gcontext
:drawable window
:foreground black
:background white))
(font (make-instance 'font :family "Times New Roman" :subfamily "Bold Italic"
:size 12 :antialiased t)))
(unwind-protect
(progn
(xlib:map-window window)
(setf (xlib:gcontext-foreground grackon) black)
(xlib:event-case (*display* :force-output-p t
:discard-p t)
(:exposure ()
(draw-text window grackon font "The quick brown fox jumps over the lazy dog." 100 100)
(when (= 0 (random 2))
(rotatef (xlib:gcontext-foreground grackon) (xlib:gcontext-background grackon)))
(draw-text window grackon font "Съешь же ещё этих мягких французских булок, да выпей чаю." 100 (+ 100 (baseline-to-baseline window font)))
(setf (font-antialiased font) (= 0 (random 2)))
(if (= 0 (random 2))
(setf (font-subfamily font) "Regular")
(setf (font-subfamily font) "Italic"))
(draw-text window grackon font "Жебракують філософи при ґанку церкви в Гадячі, ще й шатро їхнє п’яне знаємо." 100 (+ 100 (* 2 (baseline-to-baseline window font))))
(draw-text window grackon font "Press space to exit. Нажмите пробел для выхода." 100 (+ 100 (* 3 (baseline-to-baseline window font)))))
(:button-press () t)
(:key-press (code state) (char= #\Space (xlib:keycode->character *display* code state)))))
(progn
(xlib:free-gcontext grackon)
(xlib:destroy-window window)
(xlib:display-force-output *display*)))))
@end lisp
Result is
@image{example}
@node Dictionary
@comment node-name, next, previous, up
@chapter Dictionary
@include include/package-xft.texinfo
@include include/var-xft-star-font-dirs-star.texinfo
@include include/fun-xft-cache-fonts.texinfo
@include include/fun-xft-cache-font-file.texinfo
@include include/fun-xft-get-font-families.texinfo
@include include/fun-xft-get-font-subfamilies.texinfo
@include include/class-xft-font.texinfo
@include include/fun-xft-screen-default-dpi.texinfo
@include include/fun-xft-screen-dpi.texinfo
@include include/fun-xft-setf-screen-dpi.texinfo
@include include/fun-xft-font-ascent.texinfo
@include include/fun-xft-font-descent.texinfo
@include include/fun-xft-font-line-gap.texinfo
@include include/fun-xft-baseline-to-baseline.texinfo
@include include/fun-xft-text-bounding-box.texinfo
@include include/fun-xft-text-width.texinfo
@include include/fun-xft-text-height.texinfo
@include include/fun-xft-text-line-bounding-box.texinfo
@include include/fun-xft-text-line-width.texinfo
@include include/fun-xft-text-line-height.texinfo
@include include/fun-xft-xmin.texinfo
@include include/fun-xft-ymin.texinfo
@include include/fun-xft-xmax.texinfo
@include include/fun-xft-ymax.texinfo
@include include/fun-xft-draw-text.texinfo
@include include/fun-xft-draw-text-line.texinfo
@include include/fun-xft-font-lines-height.texinfo
@include include/backmatter.texinfo
@bye

BIN
doc/example.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -0,0 +1,39 @@
@node Concept Index
@comment node-name, next, previous, up
@appendix Concept Index
@printindex cp
@node Function Index
@comment node-name, next, previous, up
@appendix Function Index
@printindex fn
@node Variable Index
@comment node-name, next, previous, up
@appendix Variable Index
@printindex vr
@node Type Index
@comment node-name, next, previous, up
@appendix Type Index
@printindex tp
@node Colophon
@comment node-name, next, previous, up
@unnumbered Colophon
This manual is maintained in Texinfo, and automatically translated
into other forms (e.g. HTML or pdf). If you're @emph{reading} this
manual in one of these non-Texinfo translated forms, that's fine, but
if you want to @emph{modify} this manual, you are strongly advised to
seek out a Texinfo version and modify that instead of modifying a
translated version.

View file

@ -0,0 +1,41 @@
@tindex font
@anchor{Class font}
@deftp {Class} {font}
Class precedence list: @code{@lw{font}, @lw{standard-object}, @lw{t}}
Slots:
@itemize
@item @code{family} --- initarg: @code{@w{:family}}; reader: @code{@w{clx-truetype:font-family}}; writer: @code{@w{(setf clx-truetype:font-family)}}
Font family.
@item @code{subfamily} --- initarg: @code{@w{:subfamily}}; reader: @code{@w{clx-truetype:font-subfamily}}; writer: @code{@w{(setf clx-truetype:font-subfamily)}}
Font subfamily. For e.g. regular, italic, bold, bold italib.
@item @code{size} --- initarg: @code{@w{:size}}; reader: @code{@w{clx-truetype:font-size}}; writer: @code{@w{(setf clx-truetype:font-size)}}
Font size in points.
@item @code{underline} --- initarg: @code{@w{:underline}}; reader: @code{@w{clx-truetype:font-underline}}; writer: @code{@w{(setf clx-truetype:font-underline)}}
Draw line under text string.
@item @code{strikethrough} --- initarg: @code{@w{:strikethrough}}; reader: @code{@w{clx-truetype:font-strikethrough}}; writer: @code{@w{(setf clx-truetype:font-strikethrough)}}
Draw strike through text string.
@item @code{overline} --- initarg: @code{@w{:overline}}; reader: @code{@w{clx-truetype:font-overline}}; writer: @code{@w{(setf clx-truetype:font-overline)}}
Draw line over text string.
@item @code{background} --- initarg: @code{@w{:background}}; reader: @code{@w{clx-truetype:font-background}}; writer: @code{@w{(setf clx-truetype:font-background)}}
Background color.
@item @code{foreground} --- initarg: @code{@w{:foregroung}}; reader: @code{@w{clx-truetype:font-foregroung}}; writer: @code{@w{(setf clx-truetype:font-foregroung)}}
Foreground color.
@item @code{overwrite-gcontext} --- initarg: @code{@w{clx-truetype::overwrite-gcontext}}; reader: @code{@w{clx-truetype:font-overwrite-gcontext}}; writer: @code{@w{(setf clx-truetype:font-overwrite-gcontext)}}
Use font values for background and foreground colors.
@item @code{antialiased} --- initarg: @code{@w{clx-truetype::antialiased}}; reader: @code{@w{clx-truetype:font-antialiased}}; writer: @code{@w{(setf clx-truetype:font-antialiased)}}
Antialias text string.
@end itemize
Class for representing font information.
@end deftp

View file

@ -0,0 +1,5 @@
@findex baseline-to-baseline
@anchor{Function baseline-to-baseline}
@deffn {Function} {baseline-to-baseline} drawable font
Returns distance between baselines of @@var@{font@}. @@@{drawable@} must be window, pixmap or screen. ascent @code{-} descent @code{+} line gap
@end deffn

View file

@ -0,0 +1,5 @@
@findex cache-font-file
@anchor{Function cache-font-file}
@deffn {Function} {cache-font-file} pathname
Caches font file into hashmap.
@end deffn

View file

@ -0,0 +1,5 @@
@findex cache-fonts
@anchor{Function cache-fonts}
@deffn {Function} {cache-fonts}
Caches fonts from *font-dirs* directories.
@end deffn

View file

@ -0,0 +1,6 @@
@findex draw-text-line
@anchor{Function draw-text-line}
@deffn {Function} {draw-text-line} drawable gcontext font string x y @&key start end
Draws text string using @@var@{font@} on @@var@{drawable@} with graphic context @@var@{gcontext@}. @@var@{x@}, @@var@{y@} are the left point of base line. @@var@{start@} and @@var@{end@} are used for substring rendering.
If @@var@{gcontext@} has background color, text line bounding box will be filled with it. Text line bounding box is bigger than text bounding box. @@var@{drawable@} must be window or pixmap.
@end deffn

View file

@ -0,0 +1,6 @@
@findex draw-text
@anchor{Function draw-text}
@deffn {Function} {draw-text} drawable gcontext font string x y @&key start end
Draws text string using @@var@{font@} on @@var@{drawable@} with graphic context @@var@{gcontext@}. @@var@{x@}, @@var@{y@} are the left point of base line. @@var@{start@} and @@var@{end@} are used for substring rendering.
If @@var@{gcontext@} has background color, text bounding box will be filled with it. Text line bounding box is bigger than text bounding box. @@var@{drawable@} must be window or pixmap.
@end deffn

View file

@ -0,0 +1,5 @@
@findex font-ascent
@anchor{Function font-ascent}
@deffn {Function} {font-ascent} drawable font
Returns ascent of @@var@{font@}. @@@{drawable@} must be window, pixmap or screen.
@end deffn

View file

@ -0,0 +1,5 @@
@findex font-descent
@anchor{Function font-descent}
@deffn {Function} {font-descent} drawable font
Returns descent of @@var@{font@}. @@@{drawable@} must be window, pixmap or screen.
@end deffn

View file

@ -0,0 +1,5 @@
@findex font-line-gap
@anchor{Function font-line-gap}
@deffn {Function} {font-line-gap} drawable font
Returns line gap of @@var@{font@}. @@@{drawable@} must be window, pixmap or screen.
@end deffn

View file

@ -0,0 +1,5 @@
@findex font-lines-height
@anchor{Function font-lines-height}
@deffn {Function} {font-lines-height} drawable font lines-count
Returns text lines height in pixels. For one line height is ascender+descender. For more than one line height is ascender+descender+linegap.
@end deffn

View file

@ -0,0 +1,5 @@
@findex get-font-families
@anchor{Function get-font-families}
@deffn {Function} {get-font-families}
Returns cached font families.
@end deffn

View file

@ -0,0 +1,5 @@
@findex get-font-subfamilies
@anchor{Function get-font-subfamilies}
@deffn {Function} {get-font-subfamilies} font-family
Returns font subfamilies for current. For e.g. regular, italic, bold, etc.
@end deffn

View file

@ -0,0 +1,5 @@
@findex screen-default-dpi
@anchor{Function screen-default-dpi}
@deffn {Function} {screen-default-dpi} screen
Returns default dpi for @@var@{screen@}. pixel width @code{*} 25.4/millimeters width
@end deffn

View file

@ -0,0 +1,5 @@
@findex screen-dpi
@anchor{Function screen-dpi}
@deffn {Function} {screen-dpi} screen
Returns current dpi for @@var@{screen@}.
@end deffn

View file

@ -0,0 +1,5 @@
@findex @setf{screen-dpi}
@anchor{Function (setf screen-dpi)}
@deffn {Function} {@setf{screen-dpi}} value screen
Sets current dpi for @@var@{screen@}.
@end deffn

View file

@ -0,0 +1,5 @@
@findex text-bounding-box
@anchor{Function text-bounding-box}
@deffn {Function} {text-bounding-box} drawable font string
Returns text bounding box. @@@{drawable@} must be window, pixmap or screen. Text bounding box is only for contours. Bounding box for space (#x20) is zero.
@end deffn

View file

@ -0,0 +1,5 @@
@findex text-height
@anchor{Function text-height}
@deffn {Function} {text-height} drawable font string
Returns height of text bounding box. @@@{drawable@} must be window, pixmap or screen.
@end deffn

View file

@ -0,0 +1,5 @@
@findex text-line-bounding-box
@anchor{Function text-line-bounding-box}
@deffn {Function} {text-line-bounding-box} drawable font string
Returns text line bounding box. @@var@{drawable@} must be window, pixmap or screen. Text line bounding box is bigger than text bounding box. It's height is ascent @code{+} descent, width is sum of advance widths minus sum of kernings.
@end deffn

View file

@ -0,0 +1,5 @@
@findex text-line-height
@anchor{Function text-line-height}
@deffn {Function} {text-line-height} drawable font string
Returns height of text line bounding box. @@var@{drawable@} must be window, pixmap or screen.
@end deffn

View file

@ -0,0 +1,5 @@
@findex text-line-width
@anchor{Function text-line-width}
@deffn {Function} {text-line-width} drawable font string
Returns width of text line bounding box. @@var@{drawable@} must be window, pixmap or screen. It is sum of advance widths minus sum of kernings.
@end deffn

View file

@ -0,0 +1,5 @@
@findex text-width
@anchor{Function text-width}
@deffn {Function} {text-width} drawable font string
Returns width of text bounding box. @@@{drawable@} must be window, pixmap or screen.
@end deffn

View file

@ -0,0 +1,5 @@
@findex xmax
@anchor{Function xmax}
@deffn {Function} {xmax} bounding-box
Returns right side x of @@var@{bounding-box@}
@end deffn

View file

@ -0,0 +1,5 @@
@findex xmin
@anchor{Function xmin}
@deffn {Function} {xmin} bounding-box
Returns left side x of @@var@{bounding-box@}
@end deffn

View file

@ -0,0 +1,5 @@
@findex ymax
@anchor{Function ymax}
@deffn {Function} {ymax} bounding-box
Returns top side y of @@var@{bounding-box@}
@end deffn

View file

@ -0,0 +1,5 @@
@findex ymin
@anchor{Function ymin}
@deffn {Function} {ymin} bounding-box
Returns bottom side y of @@var@{bounding-box@}
@end deffn

View file

@ -0,0 +1,4 @@
@anchor{Package xft}
@defvr {Package} {xft}
Package contains @code{api} for TrueType text rendering using @code{clx}, XRender. Glyphs information is obtained by @code{zpb-ttf}. Font rasterization is made by @code{cl-vectors}.
@end defvr

View file

@ -0,0 +1,97 @@
@c MACHINE GENERATED FILE! Do not edit by hand!
@c See SB-TEXINFO for details.
@ifnottex
@macro &allow-other-keys
&allow-other-keys
@end macro
@macro &optional
&optional
@end macro
@macro &rest
&rest
@end macro
@macro &key
&key
@end macro
@macro &body
&body
@end macro
@end ifnottex
@macro earmuffs{name}
*\name\*
@end macro
@macro setf{name}
(setf \name\)
@end macro
@iftex
@tex
\newif\ifdash
\long\def\dashp#1{\expandafter\setnext#1-\dashphelper}
\long\def\setnext#1-{\futurelet\next\dashphelper}
\long\def\dashphelper#1\dashphelper{
\ifx\dashphelper\next\dashfalse\else\dashtrue\fi
}
\def\lw#1{\leavevmode\dashp{#1}\ifdash#1\else\hbox{#1}\fi}
@end tex
@end iftex
@macro lw{word}
@iftex
@tex
\\lw{\word\}%
@end tex
@end iftex
@ifnottex
\word\@c
@end ifnottex
@end macro
@macro refvar{name}
@ref{Variable \name\, @code{\name\}}
@end macro
@macro refmacro{name}
@ref{Macro \name\, @code{\name\}}
@end macro
@macro reffun{name}
@ref{Function \name\, @code{\name\}}
@end macro
@iftex
@macro NIL{name}
{@smallertt@phantom{concurrency:}}\name\
@end macro
@end iftex
@ifinfo
@macro NIL{name}
\name\
@end macro
@end ifinfo
@ifnottex
@ifnotinfo
@macro NIL{name}
\name\
@end macro
@end ifnotinfo
@end ifnottex
@iftex
@macro nopkg{name}
{@smallertt@phantom{concurrency:}}\name\
@end macro
@end iftex
@ifinfo
@macro nopkg{name}
\name\
@end macro
@end ifinfo
@ifnottex
@ifnotinfo
@macro nopkg{name}
\name\
@end macro
@end ifnotinfo
@end ifnottex

View file

@ -0,0 +1,21 @@
@node Function Index
@comment node-name, next, previous, up
@appendix Function Index
@printindex fn
@node Variable Index
@comment node-name, next, previous, up
@appendix Variable Index
@printindex vr
@node Type Index
@comment node-name, next, previous, up
@appendix Type Index
@printindex tp

View file

@ -0,0 +1,5 @@
@vindex @earmuffs{font-dirs}
@anchor{Variable *font-dirs*}
@defvr {Variable} {@earmuffs{font-dirs}}
List of directories, which contain TrueType fonts.
@end defvr

14
doc/style.css Normal file
View file

@ -0,0 +1,14 @@
.node { visibility:hidden; height: 0px; }
.menu { visibility:hidden; height: 0px; }
.appendix { background-color:#d3d3d3; padding: 0.2em; }
.chapter { background-color:#d3d3d3; padding: 0.2em; }
.section { background-color:#d3d3d3; padding: 0.2em; }
.settitle { background-color:#d3d3d3; }
.contents { border: 2px solid black;
margin: 1cm 1cm 1cm 1cm;
padding-left: 3mm; }
.lisp { padding: 0; margin: 0em; }
body { padding: 2em 8em; font-family: sans-serif; }
h1 { padding: 1em; text-align: center; }
li { margin: 1em; }

BIN
doc/ttf-metrics.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

740
doc/ttf-metrics.svg Normal file
View file

@ -0,0 +1,740 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="ttf-metrics.svg"
inkscape:export-filename="/home/michael/webspace/clx-truetype/doc/ttf-metrics.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4">
<marker
inkscape:stockid="Tail"
orient="auto"
refY="0.0"
refX="0.0"
id="Tail"
style="overflow:visible">
<g
id="g5732"
transform="scale(-1.2)">
<path
id="path5734"
d="M -3.8048674,-3.9585227 L 0.54352094,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;stroke-linecap:round" />
<path
id="path5736"
d="M -1.2866832,-3.9585227 L 3.0617053,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;stroke-linecap:round" />
<path
id="path5738"
d="M 1.3053582,-3.9585227 L 5.6537466,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;stroke-linecap:round" />
<path
id="path5740"
d="M -3.8048674,4.1775838 L 0.54352094,0.21974226"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;stroke-linecap:round" />
<path
id="path5742"
d="M -1.2866832,4.1775838 L 3.0617053,0.21974226"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;stroke-linecap:round" />
<path
id="path5744"
d="M 1.3053582,4.1775838 L 5.6537466,0.21974226"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8;stroke-linecap:round" />
</g>
</marker>
<marker
inkscape:stockid="Arrow2Lstart"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lstart"
style="overflow:visible">
<path
id="path5714"
style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) translate(1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;">
<path
id="path5723"
style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Sstart"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Sstart"
style="overflow:visible">
<path
id="path5708"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
transform="scale(0.2) translate(6,0)" />
</marker>
<linearGradient
id="linearGradient4237"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4239" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="342.07407"
inkscape:cy="699.43094"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:snap-page="false"
inkscape:snap-grids="true"
showguides="false"
inkscape:window-width="1614"
inkscape:window-height="976"
inkscape:window-x="100"
inkscape:window-y="43"
inkscape:window-maximized="0"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid3013" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#eeeeee;fill-opacity:1;fill-rule:evenodd;stroke:#eeeeee;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect8248"
width="740"
height="660"
x="1.2512646"
y="2.0081191"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<text
xml:space="preserve"
style="font-size:229.0243988px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="70"
y="262.36218"
id="text2985"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="70"
y="262.36218"
id="tspan2989">Li</tspan><tspan
sodipodi:role="line"
x="70"
y="548.6427"
id="tspan2993">Sp</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 35,262.36218 580,0"
id="path3005"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#ffe5e3;fill-opacity:0.48627451;fill-rule:evenodd;stroke:#ff6457;stroke-width:1;stroke-miterlimit:15;stroke-opacity:1;stroke-dasharray:none"
id="rect8079"
width="118"
height="174"
x="85"
y="378.36218"
ry="0" />
<flowRoot
xml:space="preserve"
id="flowRoot2995"
style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Sans;font-style:normal;font-weight:normal;font-size:40px;line-height:125%;letter-spacing:0px;word-spacing:0px"><flowRegion
id="flowRegion2997"><rect
id="rect2999"
width="87.88327"
height="79.802048"
x="288.90363"
y="174.53963" /></flowRegion><flowPara
id="flowPara3001"></flowPara></flowRoot> <text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="630"
y="264.36218"
id="text3015"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="630"
y="264.36218"
id="tspan3041"
style="font-size:24px">baseline</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="558"
y="554.36218"
id="text3019"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3021"
x="558"
y="554.36218"
style="font-size:24px">baseline</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 35,552.36218 505,0"
id="path3005-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:4.5,1.5;stroke-dashoffset:0"
d="m 60,312.36218 480,0"
id="path3005-25"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:6,3,1.5,3;stroke-dashoffset:0"
d="m 60,342.36218 480,0"
id="path3005-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:6,3,1.5,3;stroke-dashoffset:0"
d="m 60,52.362183 480,0"
id="path3005-3-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:#0169c9;stroke:#0169c9;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 402,52.362183 0,209.999997"
id="path3097-7"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#0169c9;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 412,62.362183 -10,-10 -10,10"
id="path3905"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#0169c9;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 412,252.36218 -10,10 -10,-10"
id="path3907"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#0169c9;fill-opacity:1;stroke:#364e59;font-family:Sans"
x="422"
y="222.36218"
id="text3909"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="422"
y="222.36218"
id="tspan3913"
style="font-size:24px;fill:#0169c9;stroke:#364e59">ascender</tspan></text>
<path
style="fill:none;stroke:#ff8000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:15;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-mid:none;marker-end:none"
d="m 402,262.36219 0,49.99999"
id="path3097-7-6"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ff8000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:12.39999962;stroke-opacity:1;stroke-dasharray:none"
d="m 412,272.36218 -10,-10 -10,10"
id="path3905-5"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ff8000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:12.39999962;stroke-opacity:1;stroke-dasharray:none"
d="m 412,302.36218 -10,10 -10,-10"
id="path3907-0"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff8000;fill-opacity:1;stroke:#ff8000;stroke-width:1;stroke-miterlimit:12.39999962;stroke-opacity:1;stroke-dasharray:none;font-family:Sans"
x="422"
y="302.36218"
id="text3976"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3978"
x="422"
y="302.36218"
style="font-size:24px;fill:#ff8000;fill-opacity:1;stroke:#ff8000;stroke-width:1;stroke-miterlimit:12.39999962;stroke-opacity:1;stroke-dasharray:none">descender</tspan></text>
<path
style="fill:none;stroke:#015a01;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 412,332.36218 -10,10 -10,-10"
id="path3907-7"
inkscape:connector-curvature="0" />
<path
style="fill:#f44800;stroke:#015a01;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 402,342.36218 0,-30"
id="path4056"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#015a01;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 412,322.36218 -10,-10 -10,10"
id="path3905-1"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#015a01;fill-opacity:1;stroke:#015a01;font-family:Sans"
x="420"
y="336.36218"
id="text4058"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4060"
x="420"
y="336.36218"
style="font-size:24px;fill:#015a01;stroke:#015a01">line gap</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 60,32.362183 60,632.36218"
id="path4131"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="40"
y="292.36218"
id="text4133"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4135"
x="40"
y="292.36218"
style="font-size:24px">0</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="40"
y="62.362183"
id="text4137"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4139"
x="40"
y="62.362183"
style="font-size:24px">y</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="593"
y="282.36218"
id="text4141"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4143"
x="593"
y="282.36218"
style="font-size:24px">x</tspan></text>
<path
style="fill:#000000;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="m 60,32.362183 5,10 -10,0 z"
id="path4145"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 625,262.36218 -10,5 0,-10 z"
id="path4145-7"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 540,552.36218 -10,5 0,-10 z"
id="path4145-7-3"
inkscape:connector-curvature="0"
inkscape:transform-center-y="-90.000003" />
<path
style="fill:none;stroke:#980101;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 370,262.36218 0,290"
id="path3097-7-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#980101;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 380,272.36218 -10,-10 -10,10"
id="path3905-0"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#980101;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 380,542.36218 -10,10 -10,-10"
id="path3907-71"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 85,542.36218 0,20"
id="path8085"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 60,557.36218 25,0"
id="path8087"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 82,554.36218 3,3 -3,4"
id="path8089"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 63,554.36218 -3,3 3,3"
id="path8091"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
x="88.571426"
y="795.21936"
id="text8097"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan8099"
x="88.571426"
y="795.21936" /></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 203,542.36218 0,20"
id="path8101"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 220,527.36218 0,70"
id="path8103"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 203,557.36218 17,0"
id="path8087-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 217,554.36218 3,3 -3,4"
id="path8089-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 206,554.36218 -3,3 3,3"
id="path8091-3"
inkscape:connector-curvature="0" />
<path
style="fill:#6600cc;stroke:#6600cc;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 60,587.36218 160,0"
id="path8162"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#6600cc;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 210,577.36218 10,10 -10,10"
id="path8164"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#6600cc;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 70,577.36218 -10,10 10,10"
id="path8166"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#6600cc;fill-opacity:1;stroke:#6600cc;font-family:Sans;-inkscape-font-specification:Sans"
x="100"
y="597.36218"
id="text8168"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan8170"
x="100"
y="597.36218"
style="font-size:12px;fill:#6600cc;stroke:#6600cc">advance width</tspan></text>
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#980101;fill-opacity:1;stroke:#980101;font-family:Sans;-inkscape-font-specification:Sans"
x="378"
y="462.36218"
id="text8172"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan8174"
x="378"
y="462.36218">baseline to baseline</tspan><tspan
sodipodi:role="line"
x="378"
y="492.36218"
id="tspan8176" /></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="40"
y="582.36218"
id="text4133-9"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4135-5"
x="40"
y="582.36218"
style="font-size:24px">0</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="510"
y="572.36218"
id="text4141-6"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4143-1"
x="510"
y="572.36218"
style="font-size:24px">x</tspan></text>
<rect
style="fill:#cff2ff;fill-opacity:0.48627451;fill-rule:evenodd;stroke:#00baff;stroke-width:1.63299322;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect8231"
width="220"
height="260"
x="60"
y="52.362183"
ry="0" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#00baff;stroke-opacity:1;font-family:Sans;-inkscape-font-specification:Sans"
x="92"
y="76.362183"
id="text8233"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan8235"
x="92"
y="76.362183">text line bbox</tspan></text>
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#ff6457;stroke-opacity:1;font-family:Sans;-inkscape-font-specification:Sans"
x="134"
y="424.36218"
id="text8237"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan8239"
x="134"
y="424.36218">glyph bbox</tspan></text>
<rect
style="fill:#c4ffe1;fill-opacity:0.48627451;fill-rule:evenodd;stroke:#00fe8f;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect8250"
width="148"
height="175"
x="92"
y="87.362183" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#00fe8f;stroke-opacity:1;font-family:Sans;-inkscape-font-specification:Sans"
x="110.28571"
y="132.36218"
id="text8252"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan8254"
x="110.28571"
y="132.36218">text bbox</tspan></text>
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
x="65"
y="567.36218"
id="text8093"
sodipodi:linespacing="100%"><tspan
sodipodi:role="line"
id="tspan8095"
x="65"
y="567.36218"
style="font-size:8px;line-height:100%">left </tspan><tspan
sodipodi:role="line"
x="65"
y="575.36218"
style="font-size:8px;line-height:100%"
id="tspan8129">side </tspan><tspan
sodipodi:role="line"
x="65"
y="583.36218"
style="font-size:8px;line-height:100%"
id="tspan8131">bearing</tspan></text>
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
x="205"
y="567.36218"
id="text8093-2"
sodipodi:linespacing="100%"><tspan
sodipodi:role="line"
id="tspan8095-4"
x="205"
y="567.36218"
style="font-size:8px;line-height:100%">right </tspan><tspan
sodipodi:role="line"
x="205"
y="575.36218"
style="font-size:8px;line-height:100%"
id="tspan8129-4">side </tspan><tspan
sodipodi:role="line"
x="205"
y="583.36218"
style="font-size:8px;line-height:100%"
id="tspan8131-7">bearing</tspan></text>
<path
style="fill:none;stroke:#015a01;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 92,242.36218 0,35"
id="path8256"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#015a01;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 240,242.36218 0,35"
id="path8256-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:#6600cc;stroke:#015a01;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 92,272.36218 148,0"
id="path8162-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#015a01;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 235,267.36218 5,5 -5,5"
id="path8164-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#015a01;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 97,267.36218 -5,5 5,5"
id="path8166-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#015a01;fill-opacity:1;stroke:#015a01;font-family:Sans;-inkscape-font-specification:Sans"
x="105"
y="292.36218"
id="text8300"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan8302"
x="105"
y="292.36218">text width</tspan></text>
<path
style="fill:none;stroke:#013397;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 60,297.36218 0,35"
id="path8256-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#013397;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 280,297.36218 0,35"
id="path8256-5-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:#6600cc;stroke:#013397;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 60,322.36218 220,0"
id="path8162-2-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#013397;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 275,317.36218 5,5 -5,5"
id="path8164-7-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#013397;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 65,317.36218 -5,5 5,5"
id="path8166-4-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<text
xml:space="preserve"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#013397;fill-opacity:1;stroke:#013397;font-family:Sans;-inkscape-font-specification:Sans"
x="90"
y="337.36218"
id="text8334"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan8336"
x="90"
y="337.36218">text line width</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.5, 1.5;stroke-dashoffset:0"
d="m 60,602.36218 480,0"
id="path3005-25-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:none;stroke:#ff8000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:15;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-mid:none;marker-end:none"
d="m 275,552.36218 0,49.99999"
id="path3097-7-6-4"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ff8000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:12.39999962;stroke-opacity:1;stroke-dasharray:none"
d="m 285,562.36218 -10,-10 -10,10"
id="path3905-5-5"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ff8000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:12.39999962;stroke-opacity:1;stroke-dasharray:none"
d="m 285,592.36218 -10,10 -10,-10"
id="path3907-0-5"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff8000;fill-opacity:1;stroke:#ff8000;stroke-width:1;stroke-miterlimit:12.39999962;stroke-opacity:1;stroke-dasharray:none;font-family:Sans"
x="290"
y="587.36218"
id="text3976-7"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3978-7"
x="290"
y="587.36218"
style="font-size:24px;fill:#ff8000;fill-opacity:1;stroke:#ff8000;stroke-width:1;stroke-miterlimit:12.39999962;stroke-opacity:1;stroke-dasharray:none">descender</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

66
font-cache.lisp Normal file
View file

@ -0,0 +1,66 @@
(in-package #:clx-truetype)
(defvar *font-dirs* #+unix (list "/usr/share/fonts/TTF/"
(namestring (merge-pathnames ".fonts/" (user-homedir-pathname))))
#+macos (list "/Library/Fonts/")
"List of directories, which contain TrueType fonts.")
;;(pushnew (xlib:font-path *display*) *font-dirs*)
(defun cache-font-file (pathname)
"Caches font file into hashmap."
(ignore-errors
(zpb-ttf:with-font-loader (font pathname)
(multiple-value-bind (hash-table exists-p)
(gethash (zpb-ttf:family-name font) *font-cache*
(make-hash-table :test 'equal))
(setf (gethash (zpb-ttf:subfamily-name font) hash-table)
pathname)
(unless exists-p
(setf (gethash (zpb-ttf:family-name font) *font-cache*)
hash-table))))))
(defun ttf-pathname-test (pathname)
(string-equal "ttf" (pathname-type pathname)))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defparameter +font-cache-filename+
#.(merge-pathnames "font-cache.sexp"
(merge-pathnames ".fonts/" (user-homedir-pathname)))))
(defun cache-fonts ()
"Caches fonts from *font-dirs* directories."
(clrhash *font-cache*)
(dolist (font-dir *font-dirs*)
(fad:walk-directory font-dir #'cache-font-file :if-does-not-exist :ignore
:test #'ttf-pathname-test))
(cl-store:store *font-cache* +font-cache-filename+))
(defun get-font-families ()
"Returns cached font families."
(let ((result (list)))
(maphash (lambda (key value)
(declare (ignorable value))
(push key result)) *font-cache*)
(nreverse result)))
(defun get-font-subfamilies (font-family)
"Returns font subfamilies for current. For e.g. regular, italic, bold, etc."
(let ((result (list)))
(maphash (lambda (family value)
(declare (ignorable family))
(when (string-equal font-family family)
(maphash (lambda (subfamily pathname)
(declare (ignorable pathname))
(push subfamily result)) value)
(return-from get-font-subfamilies
(nreverse result)))) *font-cache*)
(nreverse result)))
;; family ->
;; subfamily -> filename
;; subfamily -> filename
(defvar *font-cache*
#.(cl-store:restore +font-cache-filename+)
"Hashmap for caching font families, subfamilies and files.")

43
package.lisp Normal file
View file

@ -0,0 +1,43 @@
;;;; package.lisp
(defpackage #:clx-truetype
(:nicknames #:xft)
(:use #:cl)
(:export
:font
:font-family
:font-subfamily
:font-size
:font-underline
:font-strikethrough
:font-overline
:font-background
:font-foregroung
:font-overwrite-gcontext
:cache-font-file
:*font-dirs*
:drawable-screen
:font-ascent
:font-descent
:text-bounding-box
:xmin
:ymin
:xmax
:ymax
:screen-default-dpi
:screen-dpi
:draw-text
:draw-text-line
:get-font-families
:get-font-subfamilies
:text-height
:text-width
:text-line-bounding-box
:text-line-width
:text-line-height
:font-line-gap
:baseline-to-baseline
:font-antialiased
:font-lines-height
:cache-fonts)
(:documentation "Package contains API for TrueType text rendering using CLX, XRender. Glyphs information is obtained by ZPB-TTF. Font rasterization is made by CL-VECTORS."))

55
test/hello-world.lisp Normal file
View file

@ -0,0 +1,55 @@
;;;;
;;;; REPL playing
(defpackage #:clx-truetype-test
(:nicknames :xft-test)
(:use #:cl #:xft)
(:export show-window))
(in-package :clx-truetype-test)
(defvar *display* (xlib:open-default-display))
(defvar *screen* (xlib:display-default-screen *display*))
(defvar *root* (xlib:screen-root *screen*))
(defun show-window ()
(let* ((black (xlib:screen-black-pixel *screen*))
(white (xlib:screen-white-pixel *screen*))
(window
(xlib:create-window :parent *root* :x 0 :y 0 :width 640 :height 480
:class :input-output
:background white
:event-mask '(:key-press :key-release :exposure :button-press
:structure-notify)))
(grackon (xlib:create-gcontext
:drawable window
:foreground black
:background white))
(font (make-instance 'font :family "Times New Roman" :subfamily "Bold Italic"
:size 36 :antialiased t)))
(unwind-protect
(progn
(xlib:map-window window)
(setf (xlib:gcontext-foreground grackon) black)
(xlib:event-case (*display* :force-output-p t
:discard-p t)
(:exposure ()
(xlib:clear-area window :width (xlib:drawable-width window)
:height (xlib:drawable-height window))
(draw-text window grackon font "The quick brown fox jumps over the lazy dog." 100 100)
(when (= 0 (random 2))
(rotatef (xlib:gcontext-foreground grackon) (xlib:gcontext-background grackon)))
(draw-text window grackon font "Съешь же ещё этих мягких французских булок, да выпей чаю." 100 (+ 100 (baseline-to-baseline window font)))
(setf (font-antialiased font) (= 0 (random 2)))
(if (= 0 (random 2))
(setf (font-subfamily font) "Regular")
(setf (font-subfamily font) "Italic"))
(draw-text window grackon font "Жебракують філософи при ґанку церкви в Гадячі, ще й шатро їхнє п’яне знаємо." 100 (+ 100 (* 2 (baseline-to-baseline window font))))
(draw-text window grackon font "Press space to exit. Нажмите пробел для выхода." 100 (+ 100 (* 3 (baseline-to-baseline window font)))))
(:button-press () t)
(:key-press (code state) (char= #\Space (xlib:keycode->character *display* code state)))))
(progn
(xlib:free-gcontext grackon)
(xlib:destroy-window window)
(xlib:display-force-output *display*)))))

353
test/stumpwm-patch.lisp Normal file
View file

@ -0,0 +1,353 @@
(require :asdf)
(asdf:load-systems :clx-truetype)
(in-package :stumpwm)
(defparameter +base-font+ (make-instance 'xft:font :family "Consolas" :subfamily "Regular" :size 13))
;;(setf +base-font+ (make-instance 'xft:font :family "Consolas" :subfamily "Regular" :size 16))
;;; primitives.lisp
(defun truetype-font-height (drawable font)
(+ (xft:font-ascent drawable font)
(- (xft:font-descent drawable font))))
(defun truetype-font-height-lines (drawable font lines-count)
(if (> lines-count 0)
(+ (+ (xft:font-ascent drawable font)
(- (xft:font-descent drawable font)))
(* (1- lines-count) (+ (xft:font-ascent drawable font)
(- (xft:font-descent drawable font))
(xft:font-line-gap drawable font))))
0))
;;; help.lisp
(defvar old-display-bindings-for-keymaps (symbol-function 'display-bindings-for-keymaps))a
(defun display-bindings-for-keymaps (key-seq &rest keymaps)
(let* ((screen (current-screen))
(data (mapcan (lambda (map)
(mapcar (lambda (b) (format nil "^5*~5a^n ~a" (print-key (binding-key b)) (binding-command b))) (kmap-bindings map)))
keymaps))
(cols (ceiling (1+ (length data))
(truncate (- (head-height (current-head)) (* 2 (screen-msg-border-width screen)))
(truetype-font-height (screen-number screen) +base-font+)))))
(message-no-timeout "Prefix: ~a~%~{~a~^~%~}"
(print-key-seq key-seq)
(columnize data cols))))
(defcommand commands () ()
"List all available commands."
(let* ((screen (current-screen))
(data (all-commands))
(cols (ceiling (length data)
(truncate (- (head-height (current-head)) (* 2 (screen-msg-border-width screen)))
(truetype-font-height (screen-number screen) +base-font+)))))
(message-no-timeout "~{~a~^~%~}"
(columnize data cols))))
;;; message-window.lisp
(defvar old-show-frame-indicator (symbol-function 'show-frame-indicator))
(defun show-frame-indicator (group &optional force)
(show-frame-outline group)
;; FIXME: Arg, these tests are already done in show-frame-outline
(when (find group (mapcar 'screen-current-group *screen-list*))
(when (or force
(and (or (> (length (tile-group-frame-tree group)) 1)
(not (atom (first (tile-group-frame-tree group)))))
(not *suppress-frame-indicator*)))
(let ((frame (tile-group-current-frame group))
(w (screen-frame-window (current-screen)))
(string (if (stringp *frame-indicator-text*)
*frame-indicator-text*
(prin1-to-string *frame-indicator-text*)))
(font (screen-font (current-screen))))
;; If it's already mapped it'll appear briefly in the wrong
;; place, so unmap it first.
(xlib:unmap-window w)
(xlib:with-state (w)
(setf (xlib:drawable-x w) (+ (frame-x frame)
(truncate (- (frame-width frame)
(xft:text-line-width w +base-font+ string)) 2))
(xlib:drawable-y w) (+ (frame-display-y group frame)
(truncate (- (frame-height frame)
(truetype-font-height w +base-font+)) 2))
(xlib:window-priority w) :above))
(xlib:map-window w)
(echo-in-window w font (screen-fg-color (current-screen)) (screen-bg-color (current-screen)) string)
(reset-frame-indicator-timer)))))
(defvar old-echo-in-window (symbol-function 'echo-in-window))
(defun echo-in-window (win font fg bg string)
(let* ((gcontext (xlib:create-gcontext :drawable win
:font font
:foreground fg
:background bg))
(height (truetype-font-height win font))
(width (xft:text-line-width win font string)))
(xlib:with-state (win)
(setf (xlib:drawable-height win) height
(xlib:drawable-width win) width))
(xlib:clear-area win)
(xlib:display-finish-output *display*)
(xft:draw-text-line win gcontext +base-font+ string 0 (xft:font-ascent win +base-font+))
;; (xlib:draw-image-glyphs win gcontext 0 (xlib:font-ascent font) string :translate #'translate-id :size 16)
))
(defvar old-echo-string-list (symbol-function 'echo-string-list))
(defun echo-string-list (screen strings &rest highlights)
"Draw each string in l in the screen's message window. HIGHLIGHT is
the nth entry to highlight."
(when strings
(unless *executing-stumpwm-command*
(let ((width
(render-antialiased-strings
screen (screen-message-cc screen) *message-window-padding* 0 strings '() nil)))
(setup-message-window screen (length strings) width)
(render-antialiased-strings
screen (screen-message-cc screen) *message-window-padding* 0 strings highlights))
(setf (screen-current-msg screen)
strings
(screen-current-msg-highlights screen)
highlights)
;; Set a timer to hide the message after a number of seconds
(if *suppress-echo-timeout*
;; any left over timers need to be canceled.
(when (timer-p *message-window-timer*)
(cancel-timer *message-window-timer*)
(setf *message-window-timer* nil))
(reset-message-window-timer)))
(push-last-message screen strings highlights)
(xlib:display-finish-output *display*)
(dformat 5 "Outputting a message:~%~{ ~a~%~}" strings)
(apply 'run-hook-with-args *message-hook* strings)))
(defvar old-setup-message-window (symbol-function 'setup-message-window))
(defun setup-message-window (screen lines width)
(let ((height (truetype-font-height-lines (screen-number screen) +base-font+ lines))
(win (screen-message-window screen)))
;; Now that we know the dimensions, raise and resize it.
(xlib:with-state (win)
(setf (xlib:drawable-height win) height
(xlib:drawable-width win) (+ width (* *message-window-padding* 2))
(xlib:window-priority win) :above)
(setup-win-gravity screen win *message-window-gravity*))
(xlib:map-window win)
(incf (screen-ignore-msg-expose screen))
;; Have to flush this or the window might get cleared
;; after we've already started drawing it.
(xlib:display-finish-output *display*)))
;;; input.lisp
(defvar old-setup-input-window (symbol-function 'setup-input-window))
(defun setup-input-window (screen prompt input)
"Set the input window up to read input"
(let* ((win (screen-input-window screen))
(height (truetype-font-height win +base-font+)))
;; Window dimensions
(xlib:with-state (win)
(setf (xlib:window-priority win) :above
(xlib:drawable-height win) height))
(xlib:map-window win)
;; Draw the prompt
(draw-input-bucket screen prompt input)))
;; Ready to recieve input
(defvar old-draw-input-bucket (symbol-function 'draw-input-bucket))
(defun draw-input-bucket (screen prompt input &optional (tail "") errorp)
"Draw to the screen's input window the contents of input."
(let* ((gcontext (screen-message-gc screen))
(win (screen-input-window screen))
(prompt-width (xft:text-line-width win +base-font+ prompt)
;; (xlib:text-width (screen-font screen) prompt :translate #'translate-id)
)
(line-content (input-line-string input))
(string (if (input-line-password input)
(make-string (length line-content) :initial-element #\*)
line-content))
(text-width (xft:text-line-width win +base-font+ string)
;; (xlib:text-width (screen-font screen) string :translate #'translate-id)
)
(space-width (xft:text-line-width win +base-font+ " ")
;;(xlib:text-width (screen-font screen) " " :translate #'translate-id)
)
(tail-width (xft:text-line-width win +base-font+ tail)
;;(xlib:text-width (screen-font screen) tail :translate #'translate-id)
)
(full-text-width (+ text-width space-width))
(pos (input-line-position input))
(width (+ prompt-width
(max 100 (+ full-text-width space-width tail-width)))))
(xlib:with-state (win)
(xlib:clear-area win :x (+ *message-window-padding*
prompt-width
text-width))
(setf (xlib:drawable-width win) (+ width (* *message-window-padding* 2)))
(setup-win-gravity screen win *input-window-gravity*))
(xlib:with-state (win)
(xft:draw-text-line win gcontext +base-font+
prompt *message-window-padding* (xft:font-ascent win +base-font+))
;; (xlib:draw-image-glyphs win gcontext
;; *message-window-padding*
;; (xlib:font-ascent (screen-font screen))
;; prompt
;; :translate #'translate-id
;; :size 16)
(xft:draw-text-line win gcontext +base-font+
string (+ *message-window-padding* prompt-width)
(xft:font-ascent win +base-font+))
;; (xlib:draw-image-glyphs win gcontext
;; (+ *message-window-padding* prompt-width)
;; (xlib:font-ascent (screen-font screen))
;; string
;; :translate #'translate-id
;; :size 16)
(xft:draw-text-line win gcontext +base-font+
tail (+ *message-window-padding* prompt-width full-text-width space-width)
(xft:font-ascent win +base-font+))
;; (xlib:draw-image-glyphs win gcontext
;; (+ *message-window-padding* prompt-width full-text-width space-width)
;; (xlib:font-ascent (screen-font screen))
;; tail
;; :translate #'translate-id
;; :size 16)
;; draw a block cursor
(invert-rect screen win
(+ *message-window-padding*
prompt-width
(xft:text-line-width win +base-font+ (subseq string 0 pos))
;; (xlib:text-width (screen-font screen) (subseq string 0 pos) :translate #'translate-id)
)
0
(xft:text-line-width win +base-font+ (if (>= pos (length string))
" "
(string (char string pos))))
;; (xlib:text-width (screen-font screen) (if (>= pos (length string))
;; " "
;; (string (char string pos)))
;; :translate #'translate-id)
(truetype-font-height win +base-font+)
;; (+ (xlib:font-descent (screen-font screen))
;; (xlib:font-ascent (screen-font screen)))
)
;; draw the error
(when errorp
(invert-rect screen win 0 0 (xlib:drawable-width win) (xlib:drawable-height win))
(xlib:display-force-output *display*)
(sleep 0.05)
(invert-rect screen win 0 0 (xlib:drawable-width win) (xlib:drawable-height win))))))
;;; mode-line.lisp
(defvar old-resize-mode-line (symbol-function 'resize-mode-line))
(defun resize-mode-line (ml)
(when (eq (mode-line-mode ml) :stump)
;; This is a StumpWM mode-line
(setf (xlib:drawable-height (mode-line-window ml))
(+ (* (1+ (count #\Newline (mode-line-contents ml) :test #'equal))
(truetype-font-height (mode-line-window ml) +base-font+)
;;(font-height (xlib:gcontext-font (mode-line-gc ml)))
)
(* *mode-line-pad-y* 2))))
(setf (xlib:drawable-width (mode-line-window ml)) (- (frame-width (mode-line-head ml))
(* 2 (xlib:drawable-border-width (mode-line-window ml))))
(xlib:drawable-height (mode-line-window ml)) (min (xlib:drawable-height (mode-line-window ml))
(truncate (head-height (mode-line-head ml)) 4))
(mode-line-height ml) (+ (xlib:drawable-height (mode-line-window ml))
(* 2 (xlib:drawable-border-width (mode-line-window ml))))
(mode-line-factor ml) (- 1 (/ (mode-line-height ml)
(head-height (mode-line-head ml))))
(xlib:drawable-x (mode-line-window ml)) (head-x (mode-line-head ml))
(xlib:drawable-y (mode-line-window ml)) (if (eq (mode-line-position ml) :top)
(head-y (mode-line-head ml))
(- (+ (head-y (mode-line-head ml))
(head-height (mode-line-head ml)))
(mode-line-height ml)))))
(defvar old-redraw-mode-line (symbol-function 'redraw-mode-line))
(defun redraw-mode-line (ml &optional force)
(when (eq (mode-line-mode ml) :stump)
(let* ((*current-mode-line-formatters* *screen-mode-line-formatters*)
(*current-mode-line-formatter-args* (list ml))
(string (mode-line-format-string ml)))
(when (or force (not (string= (mode-line-contents ml) string)))
(setf (mode-line-contents ml) string)
(resize-mode-line ml)
(render-antialiased-strings (mode-line-screen ml) (mode-line-cc ml)
*mode-line-pad-x* *mode-line-pad-y*
(split-string string (string #\Newline)) '())))))
;;; color.lisp
(defun render-antialiased-strings (screen cc padx pady strings highlights &optional (draw t))
(let* ((height (xft:baseline-to-baseline (screen-number screen) +base-font+))
(width 0)
(gc (ccontext-gc cc))
(win (ccontext-win cc))
(px (ccontext-px cc))
(*foreground* nil)
(*background* nil)
(*reverse* nil)
(*color-stack* '())
(*color-map* (screen-color-map-normal screen)))
(when draw
(when (or (not px)
(/= (xlib:drawable-width px) (xlib:drawable-width win))
(/= (xlib:drawable-height px) (xlib:drawable-height win)))
(when px (xlib:free-pixmap px))
(setf px (xlib:create-pixmap :drawable win
:width (xlib:drawable-width win)
:height (xlib:drawable-height win)
:depth (xlib:drawable-depth win))
(ccontext-px cc) px))
(xlib:with-gcontext (gc :foreground (xlib:gcontext-background gc))
(xlib:draw-rectangle px gc 0 0 (xlib:drawable-width px) (xlib:drawable-height px) t)))
(loop for s in strings
;; We need this so we can track the row for each element
for i from 0 to (length strings)
do (let ((x 0) (off 0) (len (length s)))
(loop
for st = 0 then (+ en (1+ off))
as en = (position #\^ s :start st)
do (progn
(let ((en (cond ((and en (= (1+ en) len)) nil)
((and en (char= #\^ (char s (1+ en)))) (1+ en))
(t en))))
(when draw
(xft:draw-text-line px gc
+base-font+
(subseq s st en)
(+ padx x)
(+ pady (* i height)
(xft:font-ascent px +base-font+))))
(setf x
(+ x
(xft:text-line-width
(screen-number screen) +base-font+ (subseq s st en)))
width (max width x)))
(when (and en (< (1+ en) len))
;; right-align rest of string?
(if (char= #\> (char s (1+ en)))
(progn
(when draw
(setf x (- (xlib:drawable-width px) (* 2 padx)
;; get width of rest of s
(render-antialiased-strings
(screen-number screen) cc padx pady
(list (subseq s (+ en 2)))
'() nil))
width (- (xlib:drawable-width px) (* 2 padx))))
(setf off 1))
(setf off (set-color screen cc s (1+ en))))))
while en))
when (find i highlights :test 'eql)
do (when draw (invert-rect screen px
0 (* i height)
(xlib:drawable-width px)
height)))
(when draw
(xlib:copy-area px gc 0 0 (xlib:drawable-width px) (xlib:drawable-height px) win 0 0))
(set-color screen cc "n" 0)
width))