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/table/cff/cff-dict.rkt

135 lines
4.9 KiB
Racket

5 years ago
#lang debug racket/base
5 years ago
(require racket/class
racket/match
racket/list
racket/dict
xenomorph
sugar/unstable/dict
5 years ago
"cff-operand.rkt")
5 years ago
(provide CFFDict)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFDict.js
|#
5 years ago
5 years ago
(define (op->key op)
(match (car op)
5 years ago
[(list* x0 x1 _) (bitwise-ior (arithmetic-shift x0 8) x1)]
5 years ago
[val val]))
5 years ago
5 years ago
(define CFFDict%
5 years ago
(class x:base%
5 years ago
(super-new)
5 years ago
(init-field [(@name name)] [(@ops ops)])
5 years ago
(field [(@fields fields)
(for/hash ([field (in-list @ops)])
5 years ago
(values (op->key field) field))])
5 years ago
(define (decode-operands type stream ret operands)
5 years ago
(match type
5 years ago
[(? list?) (for/list ([op (in-list operands)]
[subtype (in-list type)])
5 years ago
(decode-operands subtype stream ret (list op)))]
5 years ago
[(? xenomorphic?) (send type x:decode stream ret operands)]
5 years ago
[(or 'number 'offset 'sid) (car operands)]
['boolean (if (car operands) #t #f)]
5 years ago
[_ operands]))
5 years ago
5 years ago
(define (encode-operands type stream ctx operands)
(match type
[(? list?)
(for/list ([op (in-list operands)]
[subtype (in-list type)])
5 years ago
(car (encode-operands subtype stream ctx op)))]
5 years ago
[(? xenomorphic?) type (send type x:encode operands stream ctx)]
[_ (match operands
[(? number?) (list operands)]
[(? boolean?) (list (if operands 1 0))]
[(? list?) operands]
[_ (list operands)])]))
5 years ago
5 years ago
(define/override (post-decode val)
(dict->mutable-hash val))
5 years ago
(define/augment (x:decode stream parent)
(define end (+ (pos stream) (hash-ref parent 'length)))
5 years ago
(define ret (make-hasheq))
5 years ago
;; define hidden properties
(hash-set! ret x:parent-key parent)
(hash-set! ret x:start-offset-key (pos stream))
5 years ago
;; fill in defaults
(for ([(key field) (in-hash @fields)])
5 years ago
(hash-set! ret (second field) (fourth field)))
5 years ago
(let loop ([operands null])
(when (< (pos stream) end)
(define b (read-byte stream))
5 years ago
(let bloop ([b b])
(cond
[(< b 28)
(let ([b (if (= b 12)
(bitwise-ior (arithmetic-shift b 8) (read-byte stream))
b)])
5 years ago
(match (hash-ref @fields b #false)
[#false (error 'cff-dict-decode (format "unknown operator: ~a" b))]
[field
(define val (decode-operands (third field) stream ret operands))
(unless (void? val)
(hash-set! ret (second field) val))
(loop null)]))]
5 years ago
[else
(loop (append operands (list (decode CFFOperand stream b))))]))))
5 years ago
ret)
5 years ago
(define/augment (x:size dict parent [include-pointers #true])
(define ctx
(mhasheq x:parent-key parent
x:val-key dict
x:pointer-size-key 0
x:start-offset-key (hash-ref parent x:start-offset-key 0)))
5 years ago
(+ (for*/sum ([k (in-list (sort (dict-keys @fields) <))]
[field (in-value (dict-ref @fields k))]
5 years ago
[val (in-value (dict-ref dict (second field) #false))]
#:when (and val (not (equal? val (fourth field)))))
(define operands (encode-operands (third field) #false ctx val))
5 years ago
(define operand-size (for/sum ([op (in-list operands)])
5 years ago
(send CFFOperand x:size op)))
5 years ago
(define key (if (list? (first field)) (first field) (list (first field))))
5 years ago
(+ operand-size (length key)))
5 years ago
(if include-pointers (hash-ref ctx x:pointer-size-key) 0)))
5 years ago
(define/augment (x:encode dict stream parent)
5 years ago
(define ctx (mhasheq
x:pointers-key null
x:start-offset-key (pos stream)
x:parent-key parent
x:val-key dict
x:pointer-size-key 0))
5 years ago
(hash-set! ctx x:pointer-offset-key (+ (pos stream) (x:size dict ctx #false)))
5 years ago
(for ([field (in-list @ops)])
5 years ago
(match-define (list f0 f1 f2 f3) field)
(match (dict-ref dict f1 #false)
[(or #false (== f3)) (void)]
[val
(define operands (encode-operands f2 stream ctx val))
(for ([op (in-list operands)])
(send CFFOperand x:encode op stream))
(define key (if (list? f0) f0 (list f0)))
(for ([op (in-list key)])
(encode uint8 op stream))]))
5 years ago
(let loop ([i 0])
5 years ago
(when (< i (length (hash-ref ctx x:pointers-key)))
5 years ago
(match (list-ref (hash-ref ctx x:pointers-key) i)
5 years ago
[(x:ptr type val parent) (send type x:encode val stream parent)])
5 years ago
(loop (add1 i)))))))
5 years ago
5 years ago
(define (CFFDict [name 'unknown] [ops null]) (make-object CFFDict% name ops))