700 lines
22 KiB
Racket
700 lines
22 KiB
Racket
(module afm scheme/base
|
|
(require scheme/file
|
|
scheme/list)
|
|
|
|
(provide (protect-out afm-draw-text
|
|
afm-get-text-extent
|
|
afm-expand-name
|
|
afm-glyph-exists?
|
|
afm-record-font
|
|
afm-fonts-string)
|
|
current-ps-afm-file-paths
|
|
current-ps-cmap-file-paths)
|
|
|
|
(define (report-exn exn)
|
|
(log-error (format "PostScript/AFM error: ~a~n"
|
|
(if (exn? exn)
|
|
(exn-message exn)
|
|
exn))))
|
|
|
|
(define bytes->number
|
|
(case-lambda
|
|
[(s) (bytes->number s 10)]
|
|
[(s r) (string->number (bytes->string/latin-1 s) r)]))
|
|
|
|
;; ------------------------------------------------------------
|
|
;; Paths
|
|
|
|
(define (check-paths who)
|
|
(lambda (l)
|
|
(unless (and (list? l)
|
|
(andmap path-string? l))
|
|
(raise-type-error who "list of paths/strings" l))
|
|
(map (lambda (i)
|
|
(if (string? i) (string->path i) i))
|
|
l)))
|
|
|
|
;; Paths that users can set
|
|
;; Location of .afm files:
|
|
(define afm-home
|
|
(with-handlers ([exn:fail:filesystem? (lambda (x) (current-directory))])
|
|
(collection-path "afm")))
|
|
(define current-ps-afm-file-paths
|
|
(make-parameter (path-list-string->path-list
|
|
(or (getenv "PLTAFMPATHS") "")
|
|
(list afm-home))
|
|
(check-paths 'current-post-script-afm-file-paths)))
|
|
;; Location of CMap files (for CID fonts)
|
|
(define current-ps-cmap-file-paths
|
|
(make-parameter (path-list-string->path-list
|
|
(or (getenv "PLTCMAPPATHS") "")
|
|
(list (build-path afm-home "CMap")))
|
|
(check-paths 'current-post-script-cmap-file-paths)))
|
|
|
|
(define (find-path l f)
|
|
(or (ormap (lambda (d)
|
|
(let ([f (build-path d f)])
|
|
(and (file-exists? f)
|
|
f)))
|
|
l)
|
|
(error 'find-path "file not found: ~e in dirs: ~e" f l)))
|
|
|
|
;; ------------------------------------------------------------
|
|
;; Adobe <-> Unicode
|
|
|
|
;; Table mapping PS char names (as bytes) to Unicode integers.
|
|
;; It's loaded on demand.
|
|
(define adobe-name-to-code-point #f)
|
|
;; In fact, we load a small table at first, and
|
|
;; only load the full PS char name table if needed;
|
|
;; the following flag indicated whether the full
|
|
;; table has been loaded.
|
|
(define got-long-name-list? #f)
|
|
|
|
(define (read-glyph-names gl.txt)
|
|
(let ([ht (make-hash)])
|
|
(with-handlers ([exn:fail? report-exn])
|
|
(call-with-input-file*
|
|
gl.txt
|
|
(lambda (i)
|
|
(let loop ()
|
|
(let ([l (read-bytes-line i)])
|
|
(unless (eof-object? l)
|
|
(let ([m (regexp-match #rx#"^([a-zA-Z0-9_-]+);([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])$"
|
|
l)])
|
|
(when m
|
|
(hash-set! ht
|
|
(cadr m)
|
|
(bytes->number (caddr m) 16))))
|
|
(loop)))))))
|
|
ht))
|
|
|
|
;; Reads a font-specific glyphname mapping
|
|
(define (read-font-glyphnames file)
|
|
(let-values ([(base name dir?) (split-path file)])
|
|
(let ([file (build-path base
|
|
(bytes->path
|
|
(bytes-append
|
|
(path->bytes (path-replace-suffix name #""))
|
|
#"-glyphlist.txt")))])
|
|
(if (file-exists? file)
|
|
;; Read glyph names:
|
|
(read-glyph-names file)
|
|
;; Make empty hash table:
|
|
(make-hasheq)))))
|
|
|
|
;; Reads the Adobe char name -> Unicode table
|
|
(define (read-names! gl.txt long?)
|
|
(set! adobe-name-to-code-point (read-glyph-names
|
|
(find-path (current-ps-afm-file-paths) gl.txt)))
|
|
(set! got-long-name-list? long?))
|
|
|
|
;; Maps Adobe char name to Unicode, loading the table as necesary
|
|
(define (find-unicode font-glyphnames name)
|
|
(hash-ref
|
|
font-glyphnames
|
|
name
|
|
(lambda ()
|
|
(unless adobe-name-to-code-point
|
|
(read-names! "glyphshortlist.txt" #f))
|
|
(hash-ref adobe-name-to-code-point
|
|
name
|
|
(lambda ()
|
|
(if got-long-name-list?
|
|
(let ([m (regexp-match #rx#"^uni([0-9a-fA-Z]+)" name)])
|
|
(and m
|
|
(string->number (bytes->string/latin-1 (cadr m)) 16)))
|
|
(begin
|
|
(read-names! "glyphlist.txt" #t)
|
|
(find-unicode font-glyphnames name))))))))
|
|
|
|
|
|
;; ------------------------------------------------------------
|
|
;; CMap
|
|
|
|
;; read-cmap : path hash-table-or-false -> hash-table[int->int]
|
|
;; A CMap file maps from one character encoding (e.g., Adobe-CNS1-0)
|
|
;; to another (e.g., UTF-16). We'll always have to read one
|
|
;; UTF-32 -> Adobe-CNS table (invert it), and then use that
|
|
;; when reading any other XXX -> Adobe-CNS to generate an
|
|
;; XXX -> UTF-32 table.
|
|
(define (read-cmap file to-unicode)
|
|
(let* ([ht (make-hash)]
|
|
[put! (if to-unicode
|
|
(lambda (c cns)
|
|
(hash-set! ht
|
|
c
|
|
(hash-ref to-unicode cns #f)))
|
|
(lambda (c cns)
|
|
(hash-set! ht cns c)))])
|
|
(with-handlers ([exn:fail? report-exn])
|
|
(call-with-input-file*
|
|
file
|
|
(lambda (i)
|
|
(let loop ()
|
|
(let ([l (read-bytes-line i)])
|
|
(cond
|
|
[(eof-object? l) (void)]
|
|
[(regexp-match #rx#"begincidchar" l)
|
|
(let char-loop ()
|
|
(let ([l (read-bytes-line i)])
|
|
(cond
|
|
[(regexp-match #rx#"endcidchar" l)
|
|
(loop)]
|
|
[(regexp-match #rx#"^<([0-9A-Fa-f]*)> *([0-9]+)$" l)
|
|
=> (lambda (m)
|
|
(put! (bytes->number (cadr m) 16)
|
|
(bytes->number (caddr m)))
|
|
(char-loop))]
|
|
[else
|
|
;; parse error
|
|
(char-loop)])))]
|
|
[(regexp-match #rx#"begincidrange" l)
|
|
(let range-loop ()
|
|
(let ([l (read-bytes-line i)])
|
|
(cond
|
|
[(regexp-match #rx#"endcidrange" l)
|
|
(loop)]
|
|
[(regexp-match #rx#"^<([0-9A-Fa-f]+)> <([0-9A-Fa-f]+)> *([0-9]+)$" l)
|
|
=> (lambda (m)
|
|
(let* ([end (bytes->number (caddr m) 16)])
|
|
(let loop ([start (bytes->number (cadr m) 16)]
|
|
[v (bytes->number (cadddr m))])
|
|
(put! start v)
|
|
(unless (= start end)
|
|
(loop (add1 start) (add1 v)))))
|
|
(range-loop))]
|
|
[else
|
|
;; parse error
|
|
(range-loop)])))]
|
|
[else (loop)]))))))
|
|
ht))
|
|
|
|
(define cns->unicode-table #f)
|
|
(define (read-cns->unicode!)
|
|
(set! cns->unicode-table
|
|
(read-cmap
|
|
(find-path (current-ps-cmap-file-paths) "UniCNS-UTF32-H")
|
|
#f)))
|
|
|
|
(define cmap-table (make-hash))
|
|
(define (get-cmap name)
|
|
(unless cns->unicode-table
|
|
(read-cns->unicode!))
|
|
(hash-ref
|
|
cmap-table
|
|
name
|
|
(lambda ()
|
|
(let ([t (read-cmap
|
|
(find-path (current-ps-cmap-file-paths) (bytes->path name))
|
|
cns->unicode-table)])
|
|
(hash-set! cmap-table name t)
|
|
t))))
|
|
|
|
;; ----------------------------------------
|
|
;; AFM
|
|
|
|
;; An achar is either
|
|
;; - (mcons num-or-bytes num)
|
|
;; - (mcons num-or-bytes (vector num ligature-assoc kern-assoc))
|
|
|
|
(define make-achar
|
|
(case-lambda
|
|
[(enc width ligatures)
|
|
(if (null? ligatures)
|
|
(mcons enc width)
|
|
(mcons enc (vector width ligatures null)))]
|
|
[(enc width) (mcons enc width)]))
|
|
(define achar-enc mcar) ; Number (unicode or CID) or bytes (Adobe char name)
|
|
(define achar-width ; Integer, 0 to 1000
|
|
(lambda (x)
|
|
(if (vector? (mcdr x))
|
|
(vector-ref (mcdr x) 0)
|
|
(mcdr x))))
|
|
(define achar-ligatures
|
|
(lambda (x)
|
|
(if (vector? (mcdr x))
|
|
(vector-ref (mcdr x) 1)
|
|
null)))
|
|
(define achar-kerns
|
|
(lambda (x)
|
|
(if (vector? (mcdr x))
|
|
(vector-ref (mcdr x) 2)
|
|
null)))
|
|
(define (strip-ligatures-and-kerning achar)
|
|
(make-achar (achar-enc achar) (achar-width achar)))
|
|
(define (achar-add-kern! achar v amt)
|
|
(when (not (vector? (mcdr achar)))
|
|
(set-mcdr! achar (vector (mcdr achar) null null)))
|
|
(vector-set! (mcdr achar) 2 (cons (cons v amt) (vector-ref (mcdr achar) 2))))
|
|
|
|
(define-struct font (height descent ascent internal-leading achars is-cid? char-set-name))
|
|
|
|
(define re:hex #rx#"^<[0-9a-fA-F]>$")
|
|
|
|
(define (parse-afm file)
|
|
(let ([descender #f]
|
|
[bbox-down #f]
|
|
[bbox-up #f]
|
|
[ascender #f]
|
|
[cap-height #f]
|
|
[achars (make-hash)]
|
|
[kern-pairs null]
|
|
[char-set #f]
|
|
[char-set-name #f]
|
|
[is-cid? #f]
|
|
[font-glyphnames (read-font-glyphnames file)])
|
|
(parameterize ([read-case-sensitive #f])
|
|
(call-with-input-file*
|
|
file
|
|
(lambda (i)
|
|
(let loop ()
|
|
(let ([n (read i)])
|
|
(unless (eof-object? n)
|
|
(case n
|
|
[(descender) (set! descender (read i))]
|
|
[(fontbbox) (let ([l (read i)]
|
|
[t (read i)]
|
|
[r (read i)]
|
|
[b (read i)])
|
|
(set! bbox-up b)
|
|
(set! bbox-down t))]
|
|
[(ascender) (set! ascender (read i))]
|
|
[(capheight) (set! cap-height (read i))]
|
|
[(characterset) (let ([m (regexp-match #rx#"[a-zA-Z_0-9-]+" (read-bytes-line i))])
|
|
(when m
|
|
(set! char-set-name (car m))
|
|
(set! char-set (get-cmap char-set-name))))]
|
|
[(iscidfont) (set! is-cid?
|
|
(regexp-match #rx#"true" (read-bytes-line i)))]
|
|
[(startcharmetrics)
|
|
(let ([cnt (read i)])
|
|
(let loop ()
|
|
(let ([n (read i)])
|
|
(when (or (eq? n 'c)
|
|
(eq? n 'ch))
|
|
(let ([v (read i)]
|
|
[rest (read-bytes-line i)])
|
|
(let ([nm (regexp-match #rx#"; *N +([a-zA-Z0-9_.-]+) *;" rest)]
|
|
[wm (regexp-match #rx#"; *W.?X +([0-9]+) *;" rest)])
|
|
(when (or (and (eq? n 'c)
|
|
(integer? v))
|
|
(and (eq? n 'ch)
|
|
(symbol? v)
|
|
(regexp-match #rx#"^<[0-9a-fA-F]>$" (symbol->string v))))
|
|
(let ([name (or (and nm
|
|
(if is-cid?
|
|
(bytes->number (cadr nm))
|
|
(cadr nm)))
|
|
0)])
|
|
(hash-set!
|
|
achars
|
|
(if (and char-set is-cid?)
|
|
(hash-ref char-set name 0)
|
|
(find-unicode font-glyphnames name))
|
|
(make-achar
|
|
(let ([v (if (eq? n 'c)
|
|
v
|
|
(let ([s (symbol->string v)])
|
|
(string->number (substring s 1 (sub1 (string-length s))) 16)))])
|
|
(if (= v -1)
|
|
name
|
|
v))
|
|
(or (and wm (bytes->number (cadr wm))) 500)
|
|
(extract-ligatures font-glyphnames rest)))))))
|
|
(loop)))))]
|
|
[(startkernpairs)
|
|
(let ([cnt (read i)])
|
|
(let loop ()
|
|
(let ([kp (read i)])
|
|
(when (or (eq? kp 'kp)
|
|
(eq? kp 'kpx))
|
|
(let ([v1 (parameterize ([read-case-sensitive #t])
|
|
(read i))]
|
|
[v2 (parameterize ([read-case-sensitive #t])
|
|
(read i))]
|
|
[amt (read i)])
|
|
(read-bytes-line i)
|
|
(let ([v1 (find-unicode font-glyphnames (string->bytes/utf-8 (symbol->string v1)))]
|
|
[v2 (find-unicode font-glyphnames (string->bytes/utf-8 (symbol->string v2)))])
|
|
(set! kern-pairs (cons (list v1 v2 amt) kern-pairs))))
|
|
(loop)))))]
|
|
[else (read-bytes-line i)])
|
|
(loop)))))))
|
|
(for-each (lambda (kp)
|
|
(let ([c1 (car kp)]
|
|
[c2 (cadr kp)]
|
|
[amt (caddr kp)])
|
|
(let ([achar (hash-ref achars c1 (lambda () (make-achar 0 0)))])
|
|
(achar-add-kern! achar c2 amt))))
|
|
kern-pairs)
|
|
(let* ([descender (- (or descender bbox-down 0))]
|
|
[ascender (or ascender bbox-up)]
|
|
[cap-height (or cap-height ascender bbox-up)])
|
|
(unless bbox-up
|
|
(error 'afm "bbox-up missing for ~a" file))
|
|
(unless descender
|
|
(error 'afm "bbox-down missing for ~a" file))
|
|
(make-font (+ bbox-up descender)
|
|
descender
|
|
ascender
|
|
(- bbox-up cap-height)
|
|
achars
|
|
(and char-set #t)
|
|
char-set-name))))
|
|
|
|
(define (extract-ligatures font-glyphnames rest)
|
|
(let ([m (regexp-match #rx#"; *L +([a-zA-Z0-9-]+) +([a-zA-Z0-9-]+)(.*)$" rest)])
|
|
(if m
|
|
(let ([next (cadr m)]
|
|
[ligature (caddr m)]
|
|
[rest (cadddr m)])
|
|
(cons (cons (find-unicode font-glyphnames next)
|
|
(find-unicode font-glyphnames ligature))
|
|
(extract-ligatures font-glyphnames rest)))
|
|
null)))
|
|
|
|
(define fonts (make-hash))
|
|
|
|
(define (get-font name)
|
|
(hash-ref fonts name
|
|
(lambda ()
|
|
(hash-set! fonts
|
|
name
|
|
(with-handlers ([void (lambda (exn)
|
|
(report-exn exn)
|
|
#f)])
|
|
(parse-afm
|
|
(find-path (current-ps-afm-file-paths)
|
|
(format "~a.afm" name)))))
|
|
(get-font name))))
|
|
|
|
;; ----------------------------------------
|
|
|
|
(define font-list #f)
|
|
(define (get-all-fonts)
|
|
(or font-list
|
|
(begin
|
|
(set! font-list
|
|
(apply append (map (lambda (dir)
|
|
(map (lambda (s)
|
|
(path->string (path-replace-suffix s #"")))
|
|
(filter (lambda (s)
|
|
(regexp-match #rx"[.][aA][fF][mM]$" (path->string s)))
|
|
(directory-list dir))))
|
|
(current-ps-afm-file-paths))))
|
|
font-list)))
|
|
|
|
;; ----------------------------------------
|
|
|
|
(define symbol-map
|
|
#(0 0 0 0 0 0 0 0
|
|
0 0 0 0 0 0 0 0
|
|
0 0 0 0 0 0 0 0
|
|
0 0 0 0 0 0 0 0
|
|
0 0 8704 0 8707 0 0 8717
|
|
0 0 8727 0 0 8722 0 0
|
|
0 0 0 0 0 0 0 0
|
|
0 0 0 0 0 0 0 0
|
|
8773 913 914 935 916 917 934 915
|
|
919 921 977 922 923 924 925 927
|
|
928 920 929 931 932 933 962 937
|
|
926 936 918 0 8756 0 8869 0
|
|
0 945 946 967 948 949 966 947
|
|
951 953 981 954 955 956 957 959
|
|
960 952 961 963 964 965 982 969
|
|
958 968 950 0 0 0 8764 0
|
|
0 0 0 0 0 0 0 0
|
|
0 0 0 0 0 0 0 0
|
|
0 0 0 0 0 0 0 0
|
|
0 0 0 0 0 0 0 0
|
|
0 978 8242 8804 8260 8734 402 9827
|
|
9830 9829 9824 8596 8592 8593 8594 8595
|
|
0 177 8243 8805 215 8733 8706 8729
|
|
247 8800 8801 8776 8230 9168 8212 8629
|
|
8501 8465 8476 8472 8855 8853 8709 8745
|
|
8746 8835 8839 8836 8834 8838 8712 8713
|
|
8736 8711 174 169 8482 8719 8730 8901
|
|
172 8743 8744 8660 8656 8657 8658 8659
|
|
9674 9001 174 169 8482 8721 9115 9116
|
|
9117 9121 9122 9123 9127 9128 9129 9130
|
|
8364 9002 8747 8992 9134 8993 9118 9119
|
|
9120 9124 9125 9126 9131 9132 9133 0))
|
|
|
|
(define (map-symbols sym-map? l)
|
|
(if sym-map?
|
|
(map (lambda (c)
|
|
(let ([v (char->integer c)])
|
|
(if (<= 0 v 255)
|
|
(integer->char (vector-ref symbol-map v))
|
|
c)))
|
|
l)
|
|
l))
|
|
|
|
;; ----------------------------------------
|
|
;; Draw/measure text
|
|
|
|
(define (afm-get-text-extent font-name size string kern? sym-map?)
|
|
(let* ([font (or (get-font font-name)
|
|
(make-font 1000 0 1000 1000 #hash() #f #f))]
|
|
[scale (/ size 1000.0)]
|
|
[descent (* scale (font-descent font))])
|
|
(values (* scale
|
|
(let loop ([cl (map-symbols sym-map? (string->list string))][width 0.0])
|
|
(cond
|
|
[(empty? cl) width]
|
|
[else (let ([achar (hash-ref
|
|
(font-achars font)
|
|
(char->integer (car cl))
|
|
(lambda ()
|
|
(find-substitute
|
|
(car cl)
|
|
(lambda (name font achar)
|
|
(strip-ligatures-and-kerning achar))
|
|
(lambda ()
|
|
(make-achar 0 500)))))])
|
|
(cond
|
|
[(and kern?
|
|
(pair? (cdr cl))
|
|
(assoc (char->integer (cadr cl))
|
|
(achar-ligatures achar)))
|
|
=> (lambda (p)
|
|
;; Replace the first two characters with a
|
|
;; ligature and try again
|
|
(loop (cons (integer->char (cdr p)) (cddr cl))
|
|
width))]
|
|
[(and kern?
|
|
(pair? (cdr cl))
|
|
(assoc (char->integer (cadr cl))
|
|
(achar-kerns achar)))
|
|
=> (lambda (p)
|
|
;; Reduce width by kerning amount
|
|
(loop (cdr cl)
|
|
(+ width (achar-width achar) (cdr p))))]
|
|
[else
|
|
(loop (cdr cl) (+ width (achar-width achar)))]))])))
|
|
(* scale (font-height font))
|
|
descent
|
|
(* scale (font-internal-leading font)))))
|
|
|
|
;; pen is positioned at text top-left:
|
|
(define (afm-draw-text font-name size string out kern? sym-map? used-fonts)
|
|
(let* ([l (map-symbols sym-map? (string->list string))]
|
|
[used-fonts (or used-fonts (make-hash))]
|
|
[font (or (get-font font-name)
|
|
(make-font 0 0 0 0 #hash() #f #f))]
|
|
[show-simples (lambda (simples special-font-name special-font)
|
|
(unless (null? simples)
|
|
(when special-font
|
|
(let ([name (afm-expand-name special-font-name)])
|
|
(hash-set! used-fonts name #t)
|
|
(fprintf out "currentfont~n/~a findfont~n~a scalefont setfont~n"
|
|
name
|
|
size)))
|
|
(if (font-is-cid? (or special-font font))
|
|
(fprintf out "<~a> show\n"
|
|
(apply
|
|
string-append
|
|
(map (lambda (s)
|
|
(let ([s (format "000~x" s)])
|
|
(substring s (- (string-length s) 4))))
|
|
(reverse simples))))
|
|
(let loop ([bytes (list->bytes (reverse simples))])
|
|
(unless (bytes=? #"" bytes)
|
|
(if ((bytes-length bytes) . > . 100)
|
|
;; > 100, so break up the string to avoid over-long lines:
|
|
(begin
|
|
(loop (subbytes bytes 0 100))
|
|
(loop (subbytes bytes 100)))
|
|
;; <= 100, so try to write it, but look for unsafe chars
|
|
(let ([m (regexp-match-positions #rx#"[^-a-z A-Z0-9!@#$%^&*_=+,.<>:;'\"`~|/?]+"
|
|
bytes)])
|
|
(if m
|
|
;; Encode the unsafe chars in hex
|
|
(begin
|
|
(loop (subbytes bytes 0 (caar m)))
|
|
(fprintf out "<~a> show\n"
|
|
(apply string-append
|
|
(map
|
|
(lambda (c)
|
|
(let ([s (format "0~x" c)])
|
|
(substring s (- (string-length s) 2))))
|
|
(bytes->list (subbytes bytes (caar m) (cdar m))))))
|
|
(loop (subbytes bytes (cdar m))))
|
|
;; All safe - write directly
|
|
(fprintf out "(~a) show\n" bytes)))))))
|
|
(when special-font
|
|
;; Uses result of currentfont above:
|
|
(fprintf out "setfont~n"))))])
|
|
(fprintf out "0 -~a rmoveto\n" (/ (* size (- (font-height font) (font-descent font))) 1000.0))
|
|
(let loop ([l l][simples null][special-font-name #f][special-font #f])
|
|
(cond
|
|
[(null? l)
|
|
(show-simples simples special-font-name special-font)]
|
|
[(hash-ref (font-achars font) (char->integer (car l)) #f)
|
|
=> (lambda (achar)
|
|
(if (integer? (achar-enc achar))
|
|
;; It's simple...
|
|
(if special-font
|
|
;; Flush simples using special font
|
|
(begin
|
|
(show-simples simples special-font-name special-font)
|
|
(loop (cdr l) (list (achar-enc achar)) #f #f))
|
|
;; Continue simple chain. Look ahead for ligatures and kerning:
|
|
(cond
|
|
[(and kern?
|
|
(pair? (cdr l))
|
|
(assoc (char->integer (cadr l))
|
|
(achar-ligatures achar)))
|
|
=> (lambda (p)
|
|
;; Substitute ligature
|
|
(loop (cons (integer->char (cdr p)) (cddr l))
|
|
simples
|
|
#f
|
|
#f))]
|
|
[(and kern?
|
|
(pair? (cdr l))
|
|
(assoc (char->integer (cadr l))
|
|
(achar-kerns achar)))
|
|
=> (lambda (p)
|
|
;; Output result so far
|
|
(show-simples (cons (achar-enc achar) simples)
|
|
special-font-name special-font)
|
|
;; Kern
|
|
(fprintf out "~a 0 rmoveto\n" (* size (/ (cdr p) 1000.0)))
|
|
;; Continue
|
|
(loop (cdr l) null #f #f))]
|
|
[else
|
|
;; Normal additional character
|
|
(loop (cdr l)
|
|
(cons (achar-enc achar) simples)
|
|
#f
|
|
#f)]))
|
|
;; Not simple... use glyphshow
|
|
(begin
|
|
(show-simples simples special-font-name special-font)
|
|
(fprintf out "/~a glyphshow\n" (achar-enc achar))
|
|
(loop (cdr l) null #f #f))))]
|
|
[else
|
|
(find-substitute
|
|
(car l)
|
|
(lambda (this-font-name this-font achar)
|
|
;; Found a substitute font. (Don't try to kern, though.)
|
|
(if (and (equal? special-font-name this-font-name)
|
|
(integer? (achar-enc achar)))
|
|
;; Continue special-font simple chain:
|
|
(loop (cdr l)
|
|
(cons (achar-enc achar) simples)
|
|
special-font-name
|
|
special-font)
|
|
;; End current simples, etc.
|
|
(begin
|
|
(show-simples simples special-font-name special-font)
|
|
(if (integer? (achar-enc achar))
|
|
(loop (cdr l)
|
|
(list (achar-enc achar))
|
|
this-font-name
|
|
this-font)
|
|
(begin
|
|
;; Not simple... use glyphshow
|
|
(fprintf out "gsave~n/~a findfont~n~a scalefont setfont~n"
|
|
(afm-expand-name this-font-name)
|
|
size)
|
|
(fprintf out "/~a glyphshow\n" (achar-enc achar))
|
|
(fprintf out "grestore~n")
|
|
(loop (cdr l) null #f #f))))))
|
|
(lambda ()
|
|
;; No mapping for the character anywhere.
|
|
(show-simples simples special-font-name special-font)
|
|
;; Future work: a box. For now, we just skip some space.
|
|
(fprintf out "~a 0 rmoveto\n" (/ size 2))
|
|
(loop (cdr l) null #f #f)))])))
|
|
used-fonts)
|
|
|
|
;; ----------------------------------------
|
|
;; Name expansion
|
|
|
|
;; Adds a char-set to the name, if necessary
|
|
(define (afm-expand-name font-name)
|
|
(let ([f (get-font font-name)])
|
|
(if (and f (font-char-set-name f))
|
|
(string-append font-name "-" (bytes->string/latin-1 (font-char-set-name f)))
|
|
font-name)))
|
|
|
|
|
|
(define (afm-record-font name used-fonts)
|
|
(let ([used-fonts (or used-fonts (make-hash))])
|
|
(hash-set! used-fonts name #t)
|
|
used-fonts))
|
|
|
|
(define (afm-fonts-string used-fonts)
|
|
(if (hash? used-fonts)
|
|
(let ([s (open-output-string)]
|
|
[pos 0])
|
|
(hash-for-each
|
|
used-fonts
|
|
(lambda (k v)
|
|
(let ([len (string-length k)])
|
|
(when ((+ len pos) . > . 50)
|
|
(fprintf s "\n%%+ ")
|
|
(set! pos 0))
|
|
(unless (zero? pos)
|
|
(display " " s)
|
|
(set! pos (add1 pos)))
|
|
(display k s)
|
|
(set! pos (+ pos len)))))
|
|
(get-output-string s))
|
|
""))
|
|
|
|
;; ----------------------------------------
|
|
;; Font substitution
|
|
|
|
(define (find-substitute char find-k none-k)
|
|
(let ([v (afm-glyph-exists? #f (char->integer char) #f)])
|
|
(if v
|
|
(apply find-k v)
|
|
(none-k))))
|
|
|
|
(define (afm-glyph-exists?* font-name char-val)
|
|
(let ([f (get-font font-name)])
|
|
(and f
|
|
(let ([achar (hash-ref (font-achars f)
|
|
char-val
|
|
#f)])
|
|
(and achar
|
|
(list font-name f achar))))))
|
|
|
|
(define (afm-glyph-exists? font-name char-val sym-map?)
|
|
(let ([char-val (if (and sym-map?
|
|
(<= 0 char-val 255))
|
|
(vector-ref symbol-map char-val)
|
|
char-val)])
|
|
(or (and font-name
|
|
(afm-glyph-exists?* font-name char-val))
|
|
(ormap (lambda (fn)
|
|
(afm-glyph-exists?* fn char-val))
|
|
(get-all-fonts))))))
|
|
|
|
|
|
|