add fontconfig family resolution

main
Matthew Butterick 5 years ago
parent 87e568f115
commit 0c4bbbfbe9

@ -11,8 +11,10 @@
xenomorph
racket/match
racket/list
racket/string
sugar/unstable/dict
"unsafe/harfbuzz.rkt"
"unsafe/fontconfig.rkt"
"glyph-position.rkt"
sugar/list
racket/promise)
@ -136,16 +138,47 @@ approximates
https://github.com/mbutterick/fontkit/blob/master/src/base.js
|#
(define (open-font str-or-path)
(define filename (if (path? str-or-path) (path->string str-or-path) str-or-path))
(define port (open-input-file filename))
(define (family->path fam #:bold [bold #f] #:italic [italic #f])
#|
https://www.freedesktop.org/software/fontconfig/fontconfig-user.html
Fontconfig provides a textual representation for patterns that the library can both accept and generate. The representation is in three parts, first a list of family names, second a list of point sizes and finally a list of additional properties:
<families>-<point sizes>:<name1>=<values1>:<name2>=<values2>...
|#
(define pat-str (string-join (filter values
(list fam
(and bold "bold")
(and italic "italic"))) ":"))
(define fp (fc-name-parse (string->bytes/utf-8 pat-str)))
(fc-config-substitute (fc-config-get-current) fp 'FcMatchPattern)
(fc-default-substitute fp)
(define-values (res pat) (fc-font-match #f fp))
(define h
(for/hash ([str (cdr (string-split (bytes->string/utf-8 (fc-name-unparse pat)) ":"))])
(let loop ([kv (string-split str "=")])
(match kv
[(list k) (loop (list k "True"))]
[(list k v) (values (string->symbol k)
(match v
["True" #t]
["False" #f]
[(? string->number) (string->number v)]
[val val]))]))))
(hash-ref h 'file))
(define (open-font str-or-path #:bold [bold #f] #:italic [italic #f])
;; rather than use a `probe` function,
;; just try making a font with each format and see what happens
(define str (if (path? str-or-path) (path->string str-or-path) str-or-path))
(or
(for/or ([font-constructor (in-list (list +ttf-font +woff-font))])
(for*/or ([path-string (in-list (list str (family->path str #:bold bold #:italic italic)))]
#:when (file-exists? path-string)
[port (in-value (open-input-file path-string))]
[font-constructor (in-list (list +ttf-font +woff-font))])
(with-handlers ([probe-fail? (λ (exn) #f)])
(font-constructor port)))
(error 'create-font "unknown font format")))
(raise-argument-error 'open-font "valid font" str-or-path)))
(module+ test
(require rackunit racket/struct racket/vector)

@ -0,0 +1,310 @@
#lang racket/base
(require ffi/unsafe
ffi-definer-convention
(for-syntax racket/base
racket/string
racket/syntax
syntax/parse))
(define fc-lib (ffi-lib "libfontconfig"))
(define-ffi-definer define-fc fc-lib
#:make-c-id convention:hyphen->camelcase
#:provide provide)
(define-cpointer-type _FcPattern)
(define-cpointer-type _FcObjectSet)
(define-cpointer-type _FcCharSet)
(define-cpointer-type _FcConfig)
(define-cpointer-type _FcFontSet)
(define-cpointer-type _FcLangSet)
(define-cpointer-type _FcStrSet)
(define-cpointer-type _FcStrList)
(define-cpointer-type _FcRange)
(define-cpointer-type _FcObjectType)
(define-cpointer-type _FcBlanks)
(define-cpointer-type _FcCache)
(define-cpointer-type _FcFileCache)
(define-cpointer-type _FcConstant)
(define-cpointer-type _FcAtomic)
;; see fontconfig.h for these types and definitions
(define FC-CHARSET-MAP-SIZE (/ 256 32))
(define _FcSetName (_enum '(fc-set-system = 0
fc-set-application)))
(define _FcType (_enum '(fc-type-unknown = -1
fc-type-void
fc-type-integer
fc-type-double
fc-type-bytes
fc-type-bool
fc-type-matrix
fc-type-char-set
fc-type-ftface
fc-type-lang-set)))
(define _FcResult (_enum '(fc-result-match
fc-result-no-match
fc-result-type-mismatch
fc-result-no-id
fc-result-out-of-memory)))
(define _FcLangResult (_enum '(fc-lang-equal = 0
fc-lang-different-country
fc-lang-different-territory
fc-lang-different-lang)))
(define _FcMatchKind (_enum '(FcMatchPattern = 0
FcMatchFont)))
(define _FcBool (_enum '(FcFalse = 0
FcTrue)))
(define-cstruct _FcMatrix ([xx _double] [xy _double]
[yx _double] [yy _double]))
(define-cstruct _FcValue ([type _FcType]
[u (_union _bytes _int _bool _double
_FcMatrix-pointer
_FcCharSet
_FcLangSet)]))
(define-syntax (define-fc-functions stx)
(syntax-parse stx
[(_ [racket-name:id c-type:expr] ...)
#`(begin (define-fc racket-name c-type)
...)]))
(define-fc-functions
[fc-init-load-config (_fun -> _FcConfig)]
[fc-init-load-config-and-fonts (_fun -> _FcConfig)]
[fc-init (_fun -> _bool)]
[fc-fini (_fun -> _void)]
[fc-get-version (_fun -> _int)]
[fc-init-reinitialize (_fun -> _bool)]
[fc-init-bring-upto-date (_fun -> _bool)]
[fc-pattern-create (_fun -> _FcPattern)]
[fc-pattern-duplicate (_fun _FcPattern -> _FcPattern)]
[fc-pattern-reference (_fun _FcPattern -> _void)]
[fc-pattern-destroy (_fun _FcPattern -> _void)]
[fc-pattern-equal (_fun _FcPattern _FcPattern -> _bool)]
[fc-pattern-equal-subset (_fun _FcPattern _FcPattern _FcObjectSet -> _bool)]
[fc-pattern-filter (_fun _FcPattern _FcObjectSet -> _bool)]
[fc-pattern-hash (_fun _FcPattern -> _int32)]
[fc-pattern-add (_fun _FcPattern _bytes _FcValue _bool -> _bool)]
[fc-pattern-add-weak (_fun _FcPattern _bytes _FcValue _bool -> _bool)]
[fc-pattern-get (_fun _FcPattern _bytes _int _FcValue -> _FcResult)]
;; TODO: vararg
;fc-pattern-build
[fc-pattern-del (_fun _FcPattern _bytes -> _bool)]
[fc-pattern-remove (_fun _FcPattern _bytes _int -> _bool)]
[fc-pattern-print (_fun _FcPattern -> _void)]
[fc-config-substitute (_fun _FcConfig
_FcPattern
_FcMatchKind
->
_FcBool)]
[fc-default-substitute (_fun _FcPattern -> _void)]
[fc-name-parse (_fun _bytes -> _FcPattern)]
[fc-name-unparse (_fun _FcPattern -> _bytes)]
[fc-pattern-format (_fun _FcPattern _bytes -> _string)]
[fc-font-set-create (_fun -> _FcFontSet)]
[fc-font-set-destroy (_fun _FcFontSet -> _void)]
[fc-font-set-add (_fun _FcFontSet _FcPattern -> _bool)]
[fc-font-set-list (_fun _FcConfig/null
[set : (_list i _FcFontSet)]
[_int = (length set)]
_FcPattern
_FcObjectSet
->
_FcFontSet)]
[fc-font-set-match (_fun _FcConfig/null
[set : (_list i _FcFontSet)]
[_int = (length set)]
_FcPattern
[res : (_ptr o _FcResult)]
->
[pat : _FcPattern]
->
(values res pat))]
[fc-font-match (_fun _FcConfig/null
_FcPattern
[res : (_ptr o _FcResult)]
->
[pat : _FcPattern]
->
(values res pat))]
[fc-font-set-print (_fun _FcFontSet -> _void)]
[fc-font-set-sort (_fun _FcConfig/null
[set : (_list i _FcFontSet)]
[_int = (length set)]
_FcPattern
_bool
[cs : (_list o _FcCharSet (length set))]
[res : (_ptr o _FcResult)]
->
[fs : _FcFontSet]
->
(values cs res fs))]
[fc-object-set-create (_fun -> _FcObjectSet)]
[fc-object-set-add (_fun _FcObjectSet _bytes -> _bool)]
[fc-object-set-destroy (_fun _FcObjectSet -> _void)]
;; TODO: vararg
;;fc-object-set-build
;; TODO: how to do freetype interop?
;fc-free-type-char-index
;fc-free-type-char-set
;fc-free-type-char-set-and-spacing
;fc-free-type-query
;fc-free-type-query-face
[fc-value-destroy (_fun _FcValue -> _void)]
[fc-value-save (_fun _FcValue -> _FcValue)]
[fc-value-print (_fun _FcValue -> _void)]
[fc-value-equal (_fun _FcValue _FcValue -> _bool)]
[fc-char-set-create (_fun -> _FcCharSet)]
[fc-char-set-destroy (_fun _FcCharSet -> _void)]
[fc-char-set-add-char (_fun _FcCharSet _int -> _void)]
[fc-char-set-del-char (_fun _FcCharSet _int -> _bool)]
[fc-char-set-copy (_fun _FcCharSet -> _FcCharSet)]
[fc-char-set-equal (_fun _FcCharSet _FcCharSet -> _bool)]
[fc-char-set-intersect (_fun _FcCharSet _FcCharSet -> _FcCharSet)]
[fc-char-set-union (_fun _FcCharSet _FcCharSet -> _FcCharSet)]
[fc-char-set-subtract (_fun _FcCharSet _FcCharSet -> _FcCharSet)]
[fc-char-set-merge (_fun _FcCharSet _FcCharSet _bool -> _bool)]
[fc-char-set-has-char (_fun _FcCharSet _int -> _bool)]
[fc-char-set-count (_fun _FcCharSet -> _int)]
[fc-char-set-intersect-count (_fun _FcCharSet _FcCharSet -> _int)]
[fc-char-set-subtract-count (_fun _FcCharSet _FcCharSet -> _int)]
[fc-char-set-is-subset (_fun _FcCharSet _FcCharSet -> _bool)]
[fc-char-set-first-page (_fun _FcCharSet
[map : (_list o _int FC-CHARSET-MAP-SIZE)]
[next : (_ptr io _int)]
-> [res : _int]
-> (values res next map))]
[fc-char-set-next-page (_fun _FcCharSet
[map : (_list o _int FC-CHARSET-MAP-SIZE)]
[next : (_ptr io _int)]
-> [res : _int]
-> (values res next map))]
[fc-lang-set-create (_fun -> _FcLangSet)]
[fc-lang-set-destroy (_fun _FcLangSet -> _void)]
[fc-lang-set-copy (_fun _FcLangSet -> _FcLangSet)]
[fc-lang-set-add (_fun _FcLangSet _bytes -> _bool)]
[fc-lang-set-del (_fun _FcLangSet _bytes -> _bool)]
[fc-lang-set-union (_fun _FcLangSet _FcLangSet -> _FcLangSet)]
[fc-lang-set-subtract (_fun _FcLangSet _FcLangSet -> _FcLangSet)]
[fc-lang-set-compare (_fun _FcLangSet _FcLangSet -> _FcLangResult)]
[fc-lang-set-contains (_fun _FcLangSet _FcLangSet -> _bool)]
[fc-lang-set-equal (_fun _FcLangSet _FcLangSet -> _bool)]
[fc-lang-set-hash (_fun _FcLangSet -> _int)]
[fc-lang-set-has-lang (_fun _FcLangSet _bytes -> _FcLangResult)]
[fc-get-default-langs (_fun -> _FcStrSet)]
[fc-lang-set-get-langs (_fun _FcLangSet -> _FcStrSet)]
[fc-get-langs (_fun -> _FcStrSet)]
[fc-lang-normalize (_fun _bytes -> _byte)]
[fc-lang-get-char-set (_fun _bytes -> _FcCharSet)]
[fc-matrix-copy (_fun _FcMatrix-pointer -> _FcMatrix-pointer)]
[fc-matrix-equal (_fun _FcMatrix-pointer _FcMatrix-pointer -> _bool)]
[fc-matrix-multiply (_fun _FcMatrix-pointer _FcMatrix-pointer _FcMatrix-pointer -> _void)]
[fc-matrix-rotate (_fun _FcMatrix-pointer _double _double -> _void)]
[fc-matrix-scale (_fun _FcMatrix-pointer _double _double -> _void)]
[fc-matrix-shear (_fun _FcMatrix-pointer _double _double -> _void)]
[fc-config-create (_fun -> _FcConfig)]
[fc-config-reference (_fun _FcConfig/null -> _void)]
[fc-config-destroy (_fun _FcConfig -> _void)]
[fc-config-set-current (_fun _FcConfig -> _bool)]
[fc-config-get-current (_fun -> _FcConfig)]
[fc-config-upto-date (_fun _FcConfig/null -> _bool)]
[fc-config-home (_fun _FcConfig -> _path)]
[fc-config-enable-home (_fun _bool -> _bool)]
[fc-config-build-fonts (_fun _FcConfig/null -> _bool)]
[fc-config-get-config-dirs (_fun _FcConfig/null -> _FcStrList)]
[fc-config-get-font-dirs (_fun _FcConfig/null -> _FcStrList)]
[fc-config-get-config-files (_fun _FcConfig/null -> _FcStrList)]
[fc-config-get-cache-dirs (_fun _FcConfig/null -> _FcStrList)]
[fc-config-get-fonts (_fun _FcConfig _FcSetName -> _FcFontSet/null)]
[fc-config-get-blanks (_fun _FcConfig/null -> _FcBlanks)]
[fc-config-get-rescan-interval (_fun _FcConfig/null -> _int)]
[fc-config-set-rescan-interval (_fun _FcConfig/null _int -> _bool)]
[fc-config-app-font-add-file (_fun _FcConfig _path -> _bool)]
[fc-config-app-font-add-dir (_fun _FcConfig _path -> _bool)]
[fc-config-app-font-clear (_fun _FcConfig -> _void)]
[fc-name-get-object-type (_fun _bytes -> _FcObjectType)]
[fc-name-get-constant (_fun _bytes -> _FcConstant)]
[fc-name-constant (_fun _bytes
[res : (_ptr o _int)]
-> [b : _bool]
-> (values b res))]
[fc-blanks-create (_fun -> _FcBlanks)]
[fc-blanks-destroy (_fun _FcBlanks -> _void)]
[fc-blanks-add (_fun _FcBlanks _int -> _bool)]
[fc-blanks-is-member (_fun _FcBlanks _int -> _bool)]
[fc-atomic-create (_fun _bytes -> _FcAtomic)]
[fc-atomic-lock (_fun _FcAtomic -> _bool)]
[fc-atomic-new-file (_fun _FcAtomic -> _bytes)]
[fc-atomic-orig-file (_fun _FcAtomic -> _bytes)]
[fc-atomic-replace-orig (_fun _FcAtomic -> _bool)]
[fc-atomic-delete-new (_fun _FcAtomic -> _void)]
[fc-atomic-unlock (_fun _FcAtomic -> _void)]
[fc-atomic-destroy (_fun _FcAtomic -> _void)]
[fc-file-scan (_fun _FcFontSet _FcStrSet
_FcFileCache _FcBlanks
_bytes _bool
-> _bool)]
[fc-file-is-dir (_fun _bytes -> _bool)]
[fc-dir-scan (_fun _FcFontSet _FcStrSet
_FcFileCache _FcBlanks
_bytes _bool
-> _bool)]
[fc-dir-cache-unlink (_fun _bytes _FcConfig -> _bool)]
[fc-dir-cache-valid (_fun _bytes -> _bool)]
[fc-dir-cache-load (_fun _bytes _FcConfig
[cache-file : (_ptr o _bytes)]
-> [res : _FcCache]
-> (values cache-file res))]
[fc-dir-cache-read (_fun _bytes _bool _FcConfig -> _FcCache)]
;; TODO: stat struct
;[fc-dir-cache-load-file (_fun ...)]
[fc-dir-cache-unload (_fun _FcCache -> _void)]
[fc-cache-dir (_fun _FcCache -> _bytes)]
[fc-cache-copy-set (_fun _FcCache -> _FcFontSet)]
[fc-cache-subdir (_fun _FcCache _int -> _bytes)]
[fc-cache-num-subdir (_fun _FcCache -> _int)]
[fc-cache-num-font (_fun _FcCache -> _int)]
[fc-dir-cache-clean (_fun _bytes _bool -> _bool)]
[fc-str-set-create (_fun -> _FcStrSet)]
[fc-str-set-member (_fun _FcStrSet _bytes -> _bool)]
[fc-str-set-equal (_fun _FcStrSet _FcStrSet -> _bool)]
[fc-str-set-add (_fun _FcStrSet _bytes -> _bool)]
[fc-str-set-add-filename (_fun _FcStrSet _bytes -> _bool)]
[fc-str-set-del (_fun _FcStrSet _bytes -> _bool)]
[fc-str-set-destroy (_fun _FcStrSet -> _void)]
[fc-str-list-create (_fun _FcStrSet -> _FcStrList)]
[fc-str-list-first (_fun _FcStrList -> _bytes)]
[fc-str-list-next (_fun _FcStrList -> _bytes)]
[fc-str-list-done (_fun _FcStrList -> _void)]
;; TODO: utilities?
)
;; defined as macro originally in fontconfig.h
(define (fc-matrix-init mtx)
(set-FcMatrix-xx! 1)
(set-FcMatrix-yy! 1)
(set-FcMatrix-xy! 0)
(set-FcMatrix-yx! 0))
Loading…
Cancel
Save