|
|
|
@ -5,7 +5,6 @@
|
|
|
|
|
"bbox.rkt"
|
|
|
|
|
"glyphrun.rkt"
|
|
|
|
|
"directory.rkt"
|
|
|
|
|
"db.rkt"
|
|
|
|
|
"struct.rkt"
|
|
|
|
|
"table-stream.rkt"
|
|
|
|
|
xenomorph
|
|
|
|
@ -68,82 +67,31 @@ https://github.com/mbutterick/fontkit/blob/master/src/TTFFont.js
|
|
|
|
|
(+bbox (hash-ref head-table 'xMin) (hash-ref head-table 'yMin)
|
|
|
|
|
(hash-ref head-table 'xMax) (hash-ref head-table 'yMax)))
|
|
|
|
|
|
|
|
|
|
(define current-layout-caching (make-parameter #false))
|
|
|
|
|
|
|
|
|
|
(struct hb-gid (val) #:transparent)
|
|
|
|
|
(struct hb-cluster (chars) #:transparent)
|
|
|
|
|
(struct hb-position (xad yad xoff yoff etc) #:transparent)
|
|
|
|
|
(struct hb-layout (hb-gids hb-clusters hb-positions) #:transparent)
|
|
|
|
|
|
|
|
|
|
(define hb-output (x:struct 'hb-gids (x:array #:type uint16 #:length uint16)
|
|
|
|
|
'hb-clusters (x:array #:type (x:array #:type uint16 #:length uint16) #:length uint16)
|
|
|
|
|
'hb-positions (x:array #:type (x:array #:type uint16 #:length 5) #:length uint16)))
|
|
|
|
|
|
|
|
|
|
(define (hb-layout->glyphrun font hbr)
|
|
|
|
|
(match hbr
|
|
|
|
|
[(hash-table ('hb-gids gids)
|
|
|
|
|
('hb-clusters clusters)
|
|
|
|
|
('hb-positions posns))
|
|
|
|
|
(define glyphs (for/list ([gidx (in-list gids)]
|
|
|
|
|
[cluster (in-list clusters)])
|
|
|
|
|
(get-glyph font gidx cluster)))
|
|
|
|
|
(define positions (for/list ([posn (in-list posns)])
|
|
|
|
|
(apply +glyph-position posn)))
|
|
|
|
|
(glyphrun glyphs positions)]))
|
|
|
|
|
|
|
|
|
|
(define (harfbuzz-layout font codepoints features script language)
|
|
|
|
|
|
|
|
|
|
;; 181228: disk-based caching (either with sqlite or `with-cache`) is a loser
|
|
|
|
|
;; reads & writes aren't worth it vs. recomputing
|
|
|
|
|
;; (though this is good news, as it avoids massive disk caches hanging around)
|
|
|
|
|
;; ram cache in pitfall suffices
|
|
|
|
|
|
|
|
|
|
;; todo: preserve semantics of user-features argument from pitfall, which is
|
|
|
|
|
;; #f = no features, null = default features, list = explicit features
|
|
|
|
|
(define (layout font str [features #f] [script #f] [language #f])
|
|
|
|
|
(define buf (hb-buf font))
|
|
|
|
|
(hb_buffer_reset buf)
|
|
|
|
|
(define codepoints (for/list ([c (in-string str)]) (char->integer c)))
|
|
|
|
|
(hb_buffer_add_codepoints buf codepoints)
|
|
|
|
|
(define chars (map hb_glyph_info_t-codepoint (hb_buffer_get_glyph_infos buf)))
|
|
|
|
|
(hb_shape (hb-font font) buf (map tag->hb-feature (or features null)))
|
|
|
|
|
(define gis (hb_buffer_get_glyph_infos buf))
|
|
|
|
|
(dictify 'hb-gids (map hb_glyph_info_t-codepoint gis)
|
|
|
|
|
'hb-clusters (break-at chars (map hb_glyph_info_t-cluster gis))
|
|
|
|
|
'hb-positions (map hb_glyph_position_t->list (hb_buffer_get_glyph_positions buf))))
|
|
|
|
|
|
|
|
|
|
(define layout-cache (make-hasheqv))
|
|
|
|
|
|
|
|
|
|
(define hb-input (x:struct 'font-crc uint32
|
|
|
|
|
'codepoints (x:array #:type uint16)
|
|
|
|
|
'userFeatures (x:array #:type (x:string #:length uint8))))
|
|
|
|
|
|
|
|
|
|
(define (layout-cache-key font-crc codepoints user-features . _)
|
|
|
|
|
(crc32c-bytes (encode hb-input (dictify
|
|
|
|
|
'font-crc font-crc
|
|
|
|
|
'codepoints codepoints
|
|
|
|
|
'userFeatures user-features) #f)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; Returns a GlyphRun object, which includes an array of Glyphs and GlyphPositions for the given string.
|
|
|
|
|
(init-db) ; make sure db is ready for queries
|
|
|
|
|
(define (layout font string [user-features #f] [script #f] [language #f] #:test [test #f])
|
|
|
|
|
#;((string?) ((option/c (listof symbol?)) (option/c symbol?) (option/c symbol?)) . ->*m . GlyphRun?)
|
|
|
|
|
(define (get-layout string)
|
|
|
|
|
(define codepoints (map char->integer (string->list string)))
|
|
|
|
|
(define args (list codepoints (if user-features (sort user-features symbol<?) null) script language))
|
|
|
|
|
(define key (apply layout-cache-key (ttf-font-crc font) args))
|
|
|
|
|
(hash-ref! layout-cache key
|
|
|
|
|
(λ ()
|
|
|
|
|
#;(encode hb-output (apply harfbuzz-layout font args) #f)
|
|
|
|
|
(match (get-layout-from-db key)
|
|
|
|
|
[(? bytes? res) (decode hb-output res)]
|
|
|
|
|
[_ (define new-layout (apply harfbuzz-layout font args))
|
|
|
|
|
(add-record! (cons key (encode hb-output new-layout #f)))
|
|
|
|
|
(make-hasheq new-layout)])))) ;; `dump` converts to hash
|
|
|
|
|
;; work on substrs to reuse cached pieces
|
|
|
|
|
;; caveat: no shaping / positioning that involve word spaces
|
|
|
|
|
;; todo: why does caching produce slightly different results in test files
|
|
|
|
|
;; theory: because word space is not included in shaping
|
|
|
|
|
(cond
|
|
|
|
|
[(current-layout-caching)
|
|
|
|
|
(define substrs (for/list ([substr (in-list (regexp-match* " " string #:gap-select? #t))]
|
|
|
|
|
#:when (positive? (string-length substr)))
|
|
|
|
|
substr))
|
|
|
|
|
(apply append-glyphruns (map (λ (layout) (hb-layout->glyphrun font layout)) (map get-layout substrs)))]
|
|
|
|
|
[else (if test
|
|
|
|
|
(get-layout string)
|
|
|
|
|
(hb-layout->glyphrun font (get-layout string)))]))
|
|
|
|
|
(define hb-gids (map hb_glyph_info_t-codepoint gis))
|
|
|
|
|
(define hb-clusters (break-at chars (map hb_glyph_info_t-cluster gis)))
|
|
|
|
|
(define hb-positions (map hb_glyph_position_t->list (hb_buffer_get_glyph_positions buf)))
|
|
|
|
|
(define glyphs (for/vector ([gidx (in-list hb-gids)]
|
|
|
|
|
[cluster (in-list hb-clusters)])
|
|
|
|
|
(get-glyph font gidx cluster)))
|
|
|
|
|
(define positions (for/vector ([posn (in-list hb-positions)])
|
|
|
|
|
(apply +glyph-position posn)))
|
|
|
|
|
(glyphrun glyphs positions))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#|
|
|
|
|
@ -182,7 +130,7 @@ https://github.com/mbutterick/fontkit/blob/master/src/base.js
|
|
|
|
|
(error 'create-font "unknown font format")))
|
|
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
|
(require rackunit)
|
|
|
|
|
(require rackunit racket/struct)
|
|
|
|
|
(define charter (open-font charter-path))
|
|
|
|
|
(define fira (open-font (path->string fira-path)))
|
|
|
|
|
(define otf (open-font (path->string fira-otf-path)))
|
|
|
|
@ -197,11 +145,10 @@ https://github.com/mbutterick/fontkit/blob/master/src/base.js
|
|
|
|
|
(check-equal? (font-cap-height charter) 671)
|
|
|
|
|
(check-equal? (font-x-height charter) 481)
|
|
|
|
|
(check-equal? (bbox->list (font-bbox charter)) '(-161 -236 1193 963))
|
|
|
|
|
(check-equal? (caar (hash-ref (layout charter "f" #:test #t) 'hb-positions)) 321)
|
|
|
|
|
(check-equal? (glyph-position-x-advance (car (glyphrun-positions (layout charter "f")))) 321)
|
|
|
|
|
(check-true (has-table? charter #"cmap"))
|
|
|
|
|
(check-exn exn:fail:contract? (λ () (get-table charter 'nonexistent-table-tag)))
|
|
|
|
|
(check-true
|
|
|
|
|
(let ([h (layout fira "Rifle" #:test #t)])
|
|
|
|
|
(and (equal? (hash-ref h 'hb-gids) '(227 480 732 412))
|
|
|
|
|
(equal? (hash-ref h 'hb-clusters) '((82) (105) (102 108) (101)))
|
|
|
|
|
(equal? (hash-ref h 'hb-positions) '((601 0 0 0 0) (279 0 0 0 0) (580 0 0 0 0) (547 0 0 0 0)))))))
|
|
|
|
|
(let ([gr (layout fira "Rifle")])
|
|
|
|
|
(and (equal? (map glyph-id (glyphrun-glyphs gr)) '(227 480 732 412))
|
|
|
|
|
(equal? (map struct->list (glyphrun-positions gr)) '((601 0 0 0 0) (279 0 0 0 0) (580 0 0 0 0) (547 0 0 0 0)))))))
|
|
|
|
|