You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
typesetting/fontland/fontland/subset.rkt

163 lines
6.1 KiB
Racket

6 years ago
#lang debug racket/base
6 years ago
(require racket/serialize
racket/class
racket/list
racket/match
6 years ago
racket/sequence
6 years ago
sugar/unstable/dict
sugar/unstable/js
"table/loca.rkt"
6 years ago
"table-stream.rkt"
6 years ago
"directory.rkt"
6 years ago
"struct.rkt"
6 years ago
fontland/glyph
fontland/ttf-glyph
6 years ago
xenomorph)
6 years ago
6 years ago
(provide subset +subset ttf-subset +ttf-subset subset-add-glyph! encode-to-port create-subset)
6 years ago
#|
approximates
https://github.com/devongovett/fontkit/blob/master/src/subset/Subset.js
|#
6 years ago
; glyphs = list of glyph ids in the subset
; mapping = of glyph ids to indexes in glyphs
6 years ago
(struct subset (font glyphs mapping) #:transparent #:mutable)
6 years ago
6 years ago
(define (+subset font [glyphs empty] [mapping (mhash)])
(define ss (subset font glyphs mapping))
6 years ago
(subset-add-glyph! ss 0)
6 years ago
ss)
(define (encode-to-port ss)
(define p (open-output-bytes))
(encode ss p)
p)
6 years ago
(define (subset-add-glyph! ss glyph-or-gid)
(define new-gid ((if (glyph? glyph-or-gid) glyph-id values) glyph-or-gid))
;; put the new glyph at the end of `glyphs`,
;; and put its index in the mapping
(hash-ref! (subset-mapping ss) new-gid
6 years ago
(λ ()
(set-subset-glyphs! ss (append (subset-glyphs ss) (list new-gid)))
6 years ago
(sub1 (length (subset-glyphs ss))))))
6 years ago
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/subset/TTFSubset.js
|#
6 years ago
(struct ttf-subset subset (glyf offset loca hmtx) #:transparent #:mutable)
6 years ago
6 years ago
(define (+ttf-subset font [glyphs empty] [mapping (mhash)]
[glyf #f]
[offset #f]
[loca #f]
[hmtx #f])
6 years ago
(define ss (ttf-subset font glyphs mapping glyf offset loca hmtx))
6 years ago
(subset-add-glyph! ss 0)
6 years ago
ss)
6 years ago
6 years ago
(define (create-subset font)
((if (has-table? font #"CFF_")
(error 'cff-fonts-unsupported)
+ttf-subset) font))
6 years ago
6 years ago
(define (ttf-subset-add-glyph ss gid)
6 years ago
;; glyph-decode unpacks the `glyf` table data corresponding to a certin gid.
6 years ago
;; here, it's not necessary for non-composite glyphs
;; because they just get copied entirely into the subset.
;; it's just used to detect composite glyphs and handle them specially.
6 years ago
;; so an optimization would be to detect composite / noncomposite without full glyph-decode.
6 years ago
(define glyph (get-glyph (subset-font ss) gid))
6 years ago
(define ttf-glyf-data (glyph-decode glyph))
6 years ago
6 years ago
;; get the offset to the glyph from the loca table
6 years ago
(match-define (list this-offset next-offset)
(take (drop (hash-ref (dump (get-table (subset-font ss) 'loca)) 'offsets) gid) 2))
6 years ago
6 years ago
(define port (get-table-stream (subset-font ss) 'glyf))
(pos port (+ (pos port) this-offset))
6 years ago
(define glyf-bytes (read-bytes (- next-offset this-offset) port))
6 years ago
;; if it is a compound glyph, include its components
6 years ago
(when (and ttf-glyf-data (negative? (· ttf-glyf-data numberOfContours)))
(for ([ttf-glyph-component (in-list (· ttf-glyf-data components))])
6 years ago
(define gid (subset-add-glyph! ss (ttf-glyph-component-glyph-id ttf-glyph-component)))
6 years ago
;; note: this (ttf-glyph-component-pos component) is correct. It's a field of a Component object, not a port
6 years ago
(bytes-copy! glyf-bytes (ttf-glyph-component-pos ttf-glyph-component) (send uint16be encode #f gid))))
6 years ago
6 years ago
(set-ttf-subset-glyf! ss (append (ttf-subset-glyf ss) (list glyf-bytes)))
(hash-update! (ttf-subset-loca ss) 'offsets
(λ (os)
(append os (list (ttf-subset-offset ss)))))
(hash-update! (ttf-subset-hmtx ss) 'metrics
(λ (ms) (append ms
(list (mhash 'advance (glyph-advance-width glyph)
'bearing (· (get-glyph-metrics glyph) leftBearing))))))
(set-ttf-subset-offset! ss (+ (ttf-subset-offset ss) (bytes-length glyf-bytes)))
6 years ago
(sub1 (length (ttf-subset-glyf ss))))
6 years ago
;; tables required by PDF spec:
;; head, hhea, loca, maxp, cvt, prep, glyf, hmtx, fpgm
;; additional tables required for standalone fonts:
;; name, cmap, OS/2, post
6 years ago
(define (clone-deep val) (deserialize (serialize val)))
6 years ago
6 years ago
(define (encode ss port)
6 years ago
(set-ttf-subset-glyf! ss empty)
(set-ttf-subset-offset! ss 0)
(set-ttf-subset-loca! ss (mhash 'offsets empty))
(set-ttf-subset-hmtx! ss (mhash 'metrics empty 'bearings empty))
6 years ago
;; include all the glyphs used in the document
;; not using `in-list` because we need to support adding more
;; glyphs to the array as component glyphs are discovered & enqueued
(for ([idx (in-naturals)]
6 years ago
#:break (= idx (length (subset-glyphs ss))))
(define gid (list-ref (subset-glyphs ss) idx))
(ttf-subset-add-glyph ss gid))
6 years ago
(define maxp (clone-deep (· (get-maxp-table (subset-font ss)) to-hash)))
6 years ago
(dict-set! maxp 'numGlyphs (length (ttf-subset-glyf ss)))
6 years ago
;; populate the new loca table
6 years ago
(dict-update! (ttf-subset-loca ss) 'offsets (λ (vals) (append vals (list (ttf-subset-offset ss)))))
(loca-pre-encode (ttf-subset-loca ss))
6 years ago
6 years ago
(define head (clone-deep (· (get-head-table (subset-font ss)) to-hash)))
6 years ago
(dict-set! head 'indexToLocFormat (· (ttf-subset-loca ss) version))
6 years ago
6 years ago
(define hhea (clone-deep (· (get-hhea-table (subset-font ss)) to-hash)))
6 years ago
(dict-set! hhea 'numberOfMetrics (length (· (ttf-subset-hmtx ss) metrics)))
6 years ago
6 years ago
(define table-mhash
(let ([mh (make-hasheq)])
(define kvs (list 'head head
'hhea hhea
'loca (ttf-subset-loca ss)
'maxp maxp
6 years ago
'cvt_ (get-cvt_-table (subset-font ss))
'prep (get-prep-table (subset-font ss))
6 years ago
'glyf (ttf-subset-glyf ss)
'hmtx (ttf-subset-hmtx ss)
6 years ago
'fpgm (get-fpgm-table (subset-font ss))))
6 years ago
(for ([kv (in-slice 2 kvs)])
(unless (second kv)
(error 'encode (format "missing value for ~a" (first kv))))
(hash-set! mh (first kv) (second kv)))
mh))
6 years ago
6 years ago
(send Directory encode port (mhash 'tables table-mhash))
6 years ago
6 years ago
#;(report* (bytes-length (send stream dump)) (send stream dump))
#;(report* (bytes-length (file->bytes "out.bin")) (file->bytes "out.bin"))
6 years ago
6 years ago
(void)
)
6 years ago