| 1 |
;;; cl-pdf copyright 2002-2005 Marc Battyani see license.txt for the details |
|---|
| 2 |
;;; You can reach me at marc.battyani@fractalconcept.com or marc@battyani.net |
|---|
| 3 |
;;; The homepage of cl-pdf is here: http://www.fractalconcept.com/asp/html/cl-pdf.html |
|---|
| 4 |
|
|---|
| 5 |
(in-package #:pdf) |
|---|
| 6 |
|
|---|
| 7 |
(defvar *font* nil |
|---|
| 8 |
"The current font in text mode") |
|---|
| 9 |
|
|---|
| 10 |
(defvar *font-size* nil |
|---|
| 11 |
"The current font in text mode") |
|---|
| 12 |
|
|---|
| 13 |
(defvar *font-cache* (make-hash-table :test #'equal)) |
|---|
| 14 |
|
|---|
| 15 |
(defgeneric font-descriptor (font-metrics &key embed errorp)) |
|---|
| 16 |
|
|---|
| 17 |
(defclass font () |
|---|
| 18 |
((name :accessor name :initform "helvetica" :initarg :name) |
|---|
| 19 |
(encoding :accessor encoding :initform *standard-encoding*) |
|---|
| 20 |
(hyphen-code :accessor hyphen-code :initform 0) |
|---|
| 21 |
(hyphen-char :accessor hyphen-char :initform nil) |
|---|
| 22 |
(font-metrics :accessor font-metrics) |
|---|
| 23 |
(kernings :accessor kernings :initform (make-hash-table)) |
|---|
| 24 |
(characters :accessor characters :initform (make-array 256 :initial-element nil)) |
|---|
| 25 |
(pdf-widths :accessor pdf-widths :initform (make-array 256 :initial-element 0)))) |
|---|
| 26 |
|
|---|
| 27 |
(defmethod print-object ((self font) stream) |
|---|
| 28 |
(print-unreadable-object (self stream :identity t :type t) |
|---|
| 29 |
(format stream "~a" (name self)))) |
|---|
| 30 |
|
|---|
| 31 |
(defmethod initialize-instance :after ((font font) &key encoding &allow-other-keys) |
|---|
| 32 |
(let ((font-metrics (gethash (name font) *font-metrics*))) |
|---|
| 33 |
(unless font-metrics (error "Font ~s not found" (name font))) |
|---|
| 34 |
(setf (font-metrics font) font-metrics) |
|---|
| 35 |
(unless encoding |
|---|
| 36 |
(setf (gethash (list (name font) nil) *font-cache*) font)) |
|---|
| 37 |
(setf (encoding font) |
|---|
| 38 |
(if encoding |
|---|
| 39 |
(get-encoding encoding) |
|---|
| 40 |
(extract-font-metrics-encoding font-metrics))) |
|---|
| 41 |
(if (eql (keyword-name (encoding font)) :unicode-encoding) |
|---|
| 42 |
(setf (pdf-widths font) (pdf-widths font-metrics) |
|---|
| 43 |
(characters font) (encoding-vector font-metrics) |
|---|
| 44 |
(hyphen-code font) (if (gethash "hyphen" (characters font-metrics)) |
|---|
| 45 |
(code (gethash "hyphen" (characters font-metrics))) |
|---|
| 46 |
0) |
|---|
| 47 |
(hyphen-char font) (code-char (hyphen-code font))) |
|---|
| 48 |
(loop with font-characters = (characters font-metrics) |
|---|
| 49 |
with pdf-widths = (pdf-widths font) |
|---|
| 50 |
with void-char = (gethash "VoidCharacter" font-characters) |
|---|
| 51 |
and characters = (characters font) |
|---|
| 52 |
and hyphen-code = nil |
|---|
| 53 |
for i from 0 to 255 |
|---|
| 54 |
for char-name across (char-names (encoding font)) |
|---|
| 55 |
for char = (or (gethash char-name font-characters) |
|---|
| 56 |
(aref (encoding-vector font-metrics) i) |
|---|
| 57 |
void-char) |
|---|
| 58 |
do (setf (aref characters i) char |
|---|
| 59 |
(aref pdf-widths i) (round (* 1000 (width char)))) |
|---|
| 60 |
(when (and (not hyphen-code) (string= char-name "hyphen")) |
|---|
| 61 |
(setf hyphen-code i |
|---|
| 62 |
(hyphen-code font) i |
|---|
| 63 |
(hyphen-char font) (code-char i))))) |
|---|
| 64 |
(compute-kern-pairs font) |
|---|
| 65 |
(setf (gethash (list (name font) (encoding font)) *font-cache*) font))) |
|---|
| 66 |
|
|---|
| 67 |
(defun compute-kern-pairs (font) |
|---|
| 68 |
(let ((char-to-code (make-hash-table)) |
|---|
| 69 |
(characters (characters font)) |
|---|
| 70 |
(kernings (kernings font))) |
|---|
| 71 |
(loop for c across characters |
|---|
| 72 |
for code from 0 |
|---|
| 73 |
when c do (setf (gethash c char-to-code) code)) |
|---|
| 74 |
(maphash #'(lambda (k v) |
|---|
| 75 |
(let ((code1 (gethash (car k) char-to-code)) |
|---|
| 76 |
(code2 (gethash (cdr k) char-to-code))) |
|---|
| 77 |
(when (and code1 code2) |
|---|
| 78 |
(setf (gethash (+ (* code1 65536) code2) kernings) (car v))))) |
|---|
| 79 |
(kernings (font-metrics font))))) |
|---|
| 80 |
|
|---|
| 81 |
|
|---|
| 82 |
(defgeneric get-char-metrics (char-or-code font encoding) |
|---|
| 83 |
;;; This generic allows to customize treating charset by the lisp implementation |
|---|
| 84 |
;; and is intended to replace get-char. |
|---|
| 85 |
;; Args: char-or-code Lisp character or its char-code |
|---|
| 86 |
(:method (char-or-code font encoding) |
|---|
| 87 |
(declare (ignore encoding)) |
|---|
| 88 |
(aref (characters font) |
|---|
| 89 |
(if (characterp char-or-code) (char-code char-or-code) char-or-code)))) |
|---|
| 90 |
|
|---|
| 91 |
(defmethod get-char-metrics (char font (encoding single-byte-encoding)) |
|---|
| 92 |
(aref (characters font) |
|---|
| 93 |
(if #+lispworks (lw:base-char-p char) |
|---|
| 94 |
#+(or allegro sbcl) (standard-char-p char) |
|---|
| 95 |
#-(or lispworks allegro sbcl) t |
|---|
| 96 |
(char-code char) |
|---|
| 97 |
(char-external-code char (charset encoding))))) |
|---|
| 98 |
|
|---|
| 99 |
(defmethod get-char-metrics ((code integer) font (encoding single-byte-encoding)) |
|---|
| 100 |
(let ((char (code-char code))) |
|---|
| 101 |
(aref (characters font) |
|---|
| 102 |
(if #+lispworks (lw:base-char-p char) |
|---|
| 103 |
#+(or allegro sbcl) (standard-char-p char) |
|---|
| 104 |
#-(or lispworks allegro sbcl) t |
|---|
| 105 |
code |
|---|
| 106 |
(char-external-code char (charset encoding)))))) |
|---|
| 107 |
|
|---|
| 108 |
#+unused |
|---|
| 109 |
(defun get-char (code font) |
|---|
| 110 |
(aref (characters font) code)) |
|---|
| 111 |
|
|---|
| 112 |
#+unused |
|---|
| 113 |
(defmacro force-char-code (char-or-code) |
|---|
| 114 |
(let ((char (gensym "char"))) |
|---|
| 115 |
`(let ((,char ,char-or-code)) |
|---|
| 116 |
(if (characterp ,char) (char-code ,char) ,char)))) |
|---|
| 117 |
|
|---|
| 118 |
(defun get-char-width (char-or-code font &optional font-size) |
|---|
| 119 |
(let ((char-metrics (get-char-metrics char-or-code font (encoding font)))) |
|---|
| 120 |
(if font-size (* (width char-metrics) font-size) (width char-metrics)))) |
|---|
| 121 |
|
|---|
| 122 |
(defun get-char-size (char-or-code font &optional font-size) |
|---|
| 123 |
(let* ((char-metrics (get-char-metrics char-or-code font (encoding font))) |
|---|
| 124 |
(width (width char-metrics)) |
|---|
| 125 |
(bbox (bbox char-metrics)) |
|---|
| 126 |
(ascender (aref bbox 3)) |
|---|
| 127 |
(descender (aref bbox 1))) |
|---|
| 128 |
(if font-size |
|---|
| 129 |
(values (* width font-size)(* ascender font-size)(* descender font-size)) |
|---|
| 130 |
(values width ascender descender)))) |
|---|
| 131 |
|
|---|
| 132 |
(defun get-char-italic-correction (char-or-code font &optional font-size) |
|---|
| 133 |
(let* ((char-metrics (get-char-metrics char-or-code font (encoding font))) |
|---|
| 134 |
(left (left-italic-correction char-metrics)) |
|---|
| 135 |
(right (right-italic-correction char-metrics))) |
|---|
| 136 |
(if font-size |
|---|
| 137 |
(values (* left font-size)(* right font-size)) |
|---|
| 138 |
(values left right)))) |
|---|
| 139 |
|
|---|
| 140 |
(defun get-font-italic-correction (font &optional font-size) |
|---|
| 141 |
(let* ((italic-sin (italic-sin (font-metrics font))) |
|---|
| 142 |
(left (* italic-sin (ascender (font-metrics font)))) |
|---|
| 143 |
(right (* italic-sin (descender (font-metrics font))))) |
|---|
| 144 |
(if font-size |
|---|
| 145 |
(values (* left font-size)(* right font-size)) |
|---|
| 146 |
(values left right)))) |
|---|
| 147 |
|
|---|
| 148 |
(defun get-kerning (char1 char2 font &optional font-size) |
|---|
| 149 |
(let* ((encoding (encoding font)) |
|---|
| 150 |
(char-metrics1 (get-char-metrics char1 font encoding)) |
|---|
| 151 |
(char-metrics2 (get-char-metrics char2 font encoding)) |
|---|
| 152 |
(kerning (gethash (+ (ash (code char-metrics1) 16) (code char-metrics2)) |
|---|
| 153 |
(kernings font) |
|---|
| 154 |
0))) |
|---|
| 155 |
(if font-size (* font-size kerning) kerning))) |
|---|
| 156 |
|
|---|
| 157 |
(defun get-font (&optional (name "helvetica") (encoding *default-encoding*)) |
|---|
| 158 |
(setf name (string-downcase name)) |
|---|
| 159 |
(let ((font-metrics (gethash name *font-metrics*))) |
|---|
| 160 |
(when (typep font-metrics 'ttu-font-metrics) |
|---|
| 161 |
(setf encoding *unicode-encoding*))) |
|---|
| 162 |
(let ((font (gethash (list name (get-encoding encoding)) *font-cache*))) |
|---|
| 163 |
(if font |
|---|
| 164 |
font |
|---|
| 165 |
(make-instance 'font :name name :encoding encoding)))) |
|---|
| 166 |
|
|---|
| 167 |
(defun clear-font-cache () |
|---|
| 168 |
(clrhash *font-cache*)) |
|---|
| 169 |
|
|---|
| 170 |
(defvar %fonts-loaded% nil) |
|---|
| 171 |
|
|---|
| 172 |
(defun load-fonts (&optional force) |
|---|
| 173 |
(when (or (not %fonts-loaded%) force) |
|---|
| 174 |
(dolist (font-dir *afm-files-directories*) |
|---|
| 175 |
(map nil 'read-afm-file (directory (merge-pathnames font-dir "*.afm")))) |
|---|
| 176 |
(clear-font-cache) |
|---|
| 177 |
(setf %fonts-loaded% t))) |
|---|
| 178 |
|
|---|
| 179 |
(eval-when (:load-toplevel :execute) |
|---|
| 180 |
(load-fonts)) |
|---|