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/pitfall/fontkit/subset.rkt

149 lines
5.0 KiB
Racket

#lang fontkit/racket
7 years ago
(require "clone.rkt" "ttfglyphencoder.rkt" "loca.rkt" "directory.rkt" xenomorph)
(provide Subset CFFSubset TTFSubset)
#|
approximates
https://github.com/devongovett/fontkit/blob/master/src/subset/Subset.js
|#
(define-subclass object% (Subset font)
(field [glyphs empty] ; list of glyph ids in the subset
[mapping (mhash)] ; mapping of glyph ids to indexes in `glyphs`
)
(send this includeGlyph 0) ; always include the missing glyph in subset
7 years ago
(define/public (encode-to-port)
(define p (open-output-bytes))
(encode this p)
p)
(as-methods
includeGlyph))
(define/contract (includeGlyph this glyph)
((or/c object? index?) . ->m . index?)
(let ([glyph (if (object? glyph) (· glyph id) glyph)])
(hash-ref! (· this mapping) glyph
(λ ()
;; put the new glyph at the end of `glyphs`,
;; and put its index in the mapping
(push-end-field! glyphs this glyph)
(sub1 (length (· this glyphs)))))))
(define-subclass Subset (CFFSubset)
(error 'cff-subset-unimplemented))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/subset/TTFSubset.js
|#
(define-subclass Subset (TTFSubset)
(field [glyphEncoder (make-object TTFGlyphEncoder)])
(field [glyf #f]
[offset #f]
[loca #f]
[hmtx #f])
(as-methods
_addGlyph
7 years ago
encode))
(define/contract (_addGlyph this gid)
7 years ago
(index? . ->m . index?)
(define glyph (send (· this font) getGlyph gid))
7 years ago
;; _decode unpacks the `glyf` table data corresponding to a certin gid.
;; 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.
;; so an optimization would be to detect composite / noncomposite without full _decode.
(define glyf (send glyph _decode))
;; get the offset to the glyph from the loca table
7 years ago
(match-define (list curOffset nextOffset) (take (drop (· this font loca offsets) gid) 2))
7 years ago
7 years ago
(define port (send (· this font) _getTableStream 'glyf))
(pos port (+ (pos port) curOffset))
7 years ago
7 years ago
(define buffer (read-bytes (- nextOffset curOffset) port))
7 years ago
;; if it is a compound glyph, include its components
(when (and glyf (negative? (· glyf numberOfContours)))
(for ([component (in-list (· glyf components))])
(define gid (send this includeGlyph (· component glyphID)))
7 years ago
;; note: this (· component pos) is correct. It's a field of a Component object, not a port
(bytes-copy! buffer (· component pos) (send uint16be encode #f gid))))
7 years ago
;; skip variation shit
(push-end-field! glyf this buffer)
7 years ago
(hash-update! (get-field loca this) 'offsets (λ (os)
(append os (list (get-field offset this)))))
7 years ago
(hash-update! (get-field hmtx this) 'metrics (λ (ms) (append ms
(list (mhash 'advance (· glyph advanceWidth)
'bearing (· (send glyph _getMetrics) leftBearing))))))
7 years ago
(increment-field! offset this (bytes-length buffer))
(sub1 (length (· this glyf))))
;; 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
7 years ago
(define/contract (encode this port)
(output-port? . ->m . void?)
7 years ago
(set-field! glyf this empty)
(set-field! offset this 0)
(set-field! loca this (mhash 'offsets empty))
(set-field! hmtx this (mhash 'metrics empty 'bearings empty))
;; 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)]
#:break (= idx (length (· this glyphs))))
(define gid (list-ref (· this glyphs) idx))
7 years ago
(send this _addGlyph gid))
7 years ago
(define maxp (cloneDeep (· this font maxp to-hash)))
(dict-set! maxp 'numGlyphs (length (· this glyf)))
7 years ago
7 years ago
;; populate the new loca table
(dict-update! (· this loca) 'offsets (λ (vals) (append vals (list (· this offset)))))
(loca-pre-encode (· this loca))
7 years ago
(define head (cloneDeep (· this font head to-hash)))
(dict-set! head 'indexToLocFormat (· this loca version))
7 years ago
(define hhea (cloneDeep (· this font hhea to-hash)))
(dict-set! hhea 'numberOfMetrics (length (· this hmtx metrics)))
7 years ago
7 years ago
(send Directory encode port
(mhash 'tables
(mhash
'head head
'hhea hhea
'loca (· this loca)
'maxp maxp
7 years ago
'cvt_ (· this font cvt_)
'prep (· this font prep)
'glyf (· this glyf)
7 years ago
'hmtx (· this hmtx)
7 years ago
'fpgm (· this font fpgm))))
7 years ago
#;(report* (bytes-length (send stream dump)) (send stream dump))
#;(report* (bytes-length (file->bytes "out.bin")) (file->bytes "out.bin"))
7 years ago
7 years ago
(void)
)