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

129 lines
4.2 KiB
Racket

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

#lang debug racket/base
(require racket/class racket/match racket/list racket/dict xenomorph sugar/unstable/dict
"cff-operand.rkt")
(provide CFFDict)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFDict.js
|#
(define (op->key op)
(match (car op)
[(list* 0th 1st _) (bitwise-ior (arithmetic-shift 0th 8) 1st)]
[val val]))
(define (key->op key)
(list (list (bitwise-and (arithmetic-shift key -8) 255) (bitwise-and key 255))))
(define CFFDict%
(class x:base%
(super-new)
(init-field [(@name name)] [(@ops ops)])
(field [(@fields fields)
(for/hash ([field (in-list @ops)])
(define key (op->key field))
(values key field))])
(define (decodeOperands type stream ret operands)
(match type
[(? list?)
(for/list ([(op i) (in-indexed operands)])
(decodeOperands (list-ref type i) stream ret (list op)))]
[(? xenomorphic?) (send type decode stream ret operands)]
[(or 'number 'offset 'sid) (car operands)]
['boolean (if (car operands) #t #f)]
[_ operands]))
(define (encodeOperands type stream ctx operands)
(cond
[(list? type)
(for/list ([(op i) (in-indexed operands)])
(car (encodeOperands (list-ref type i) stream ctx op)))]
[(xenomorphic? type) type (send type encode operands stream ctx)]
[(number? operands) (list operands)]
[(boolean? operands) (list (if operands 1 0))]
[(list? operands) operands]
[else (list operands)]))
(define/override (post-decode val)
(dict->mutable-hash val))
(augment [@decode decode])
(define (@decode stream parent)
(define end (+ (pos stream) (hash-ref parent 'length)))
(define ret (make-hash))
(define operands null)
;; define hidden properties
(hash-set! ret x:parent-key parent)
(hash-set! ret x:start-offset-key (pos stream))
;; fill in defaults
(for ([(key field) (in-hash @fields)])
(hash-set! ret (second field) (fourth field)))
(let loop ()
(when (< (pos stream) end)
(define b (read-byte stream))
(cond
[(< b 28)
(when (= b 12)
(set! b (bitwise-ior (arithmetic-shift b 8) (read-byte stream))))
(define field (hash-ref @fields b #false))
(unless field
(error 'cff-dict-decode (format "unknown operator: ~a" b)))
(define val (decodeOperands (third field) stream ret operands))
(unless (void? val)
;; ignoring PropertyDescriptor nonsense
(hash-set! ret (second field) val))
(set! operands null)]
[else
(set! operands (append operands (list (decode CFFOperand stream b))))])
(loop)))
ret)
(augment [@size size])
(define (@size dict parent [includePointers #true])
#RRR @name
#RRR includePointers
(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)))
(define len 0)
(for* ([k (in-list (sort (dict-keys @fields) <))]
[field (in-value (dict-ref @fields k))]
[val (in-value (dict-ref dict (list-ref field 1) #false))]
#:unless (let ([ res (or (not val) (equal? val (list-ref field 3)))])
res))
#R k
#R len
(define operands (encodeOperands (list-ref field 2) #f ctx val))
(set! len (+ len
(for/sum ([op (in-list operands)])
(size CFFOperand op))))
(define key (if (list? (list-ref field 0))
(list-ref field 0)
(list (list-ref field 0))))
(set! len (+ len #R (length key))))
(when #R includePointers
(set! len (+ len #R (hash-ref ctx x:pointer-size-key))))
(define final-len len)
#R final-len)
(augment [@encode encode])
(define (@encode dict stream parent)
(error 'cff-dict-encode-undefined))))
(define (CFFDict [name 'unknown] [ops null]) (make-object CFFDict% name ops))