restructify fonts

main
Matthew Butterick 6 years ago
parent d1c8b93f7b
commit 86872eaef7

@ -24,6 +24,17 @@
image-registry
output-path) #:transparent #:mutable)
(struct pdf-font (name
id
ascender
descender
line-gap
bbox
ref
embedded
embed
encode
measure-string) #:transparent #:mutable)
;; for JPEG and PNG
(struct $img (data label width height ref embed-proc) #:transparent #:mutable)

@ -1,219 +0,0 @@
#lang debug racket/base
(require
"core.rkt"
"reference.rkt"
racket/class
racket/match
racket/string
racket/format
racket/list
racket/dict
sugar/unstable/dict
"font-base.rkt"
fontland)
(provide embedded-font%)
#|
approximates
https://github.com/mbutterick/pdfkit/blob/master/lib/font/embedded.coffee
|#
(define width-cache (make-hash))
(define-syntax-rule (sum-flags [COND VAL] ...)
(for/sum ([c (in-list (list COND ...))]
[v (in-list (list VAL ...))]
#:when c)
v))
(define (to-hex . codepoints)
(string-append*
(for/list ([code (in-list codepoints)])
(~r code #:base 16 #:min-width 4 #:pad-string "0"))))
(define embedded-font%
(class pdf-font%
(init-field name-in [id #f])
(field [font (cond
[(string? name-in) (open-font name-in)]
[(path? name-in) (open-font (path->string name-in))])]
[subset (create-subset font)]
;; we make `unicode` and `width` fields integer-keyed hashes not lists
;; because they offer better random access and growability
[unicode (mhasheqv 0 '(0))] ; always include the missing glyph (gid = 0)
[widths (mhasheqv 0 (glyph-advance-width (get-glyph font 0)))]
;; always include the width of the missing glyph (gid = 0)
[name (font-postscript-name font)]
[scale (/ 1000 (font-units-per-em font))])
(super-new [ascender (* (font-ascent font) scale)]
[descender (* (font-descent font) scale)]
[bbox (font-bbox font)]
[line-gap (* (font-linegap font) scale)])
(inherit-field [@ascender ascender]
[@bbox bbox]
[@descender descender]
[@ref ref])
(define/override (string-width str size [features null])
; #f disables features ; null enables default features ; list adds features
(define scale (/ size (+ (font-units-per-em font) 0.0)))
;; use `encode` because it's cached.
;; we assume that the side effects of `encode`
;; (e.g., appending to `widths` and `unicode`)
;; are ok because every string that gets measured is going to be encoded eventually
(match-define (list _ posns) (encode str features))
(define width (for/sum ([p (in-vector posns)]) (glyph-position-x-advance p)))
(* width scale))
(define encoding-cache (make-hash))
;; called from text.rkt
(define/override (encode str [features-in null])
(define features (sort (remove-duplicates features-in) bytes<? #:key car))
(hash-ref! encoding-cache (cons str features)
(λ ()
(define glyph-run (layout font str #:features features))
(define glyphs (glyphrun-glyphs glyph-run))
(define positions (glyphrun-positions glyph-run))
(define len (vector-length glyphs))
(define subset-idxs (make-vector len))
(define new-positions (make-vector len))
(for ([glyph (in-vector glyphs)]
[posn (in-vector positions)]
[idx (in-range len)])
(define gid (subset-add-glyph! subset (glyph-id glyph)))
(define subset-idx (to-hex gid))
(vector-set! subset-idxs idx subset-idx)
(set-glyph-position-advance-width! posn (glyph-advance-width glyph))
(scale-glyph-position! posn scale)
(vector-set! new-positions idx posn)
(hash-ref! widths gid (λ () (glyph-position-advance-width posn)))
(hash-ref! unicode gid (λ () (glyph-codepoints glyph))))
(list subset-idxs new-positions))))
(define/override (embed)
;; no CFF support
(define isCFF #false) #;(is-a? subset CFFSubset)
(define font-file (make-ref))
(when isCFF
(dict-set! font-file 'Subtype 'CIDFontType0C))
(ref-write font-file (get-output-bytes (encode-to-port subset)))
(ref-end font-file)
(define family-class
(if (has-table? font 'OS/2)
(floor (/ (hash-ref (get-OS/2-table font) 'sFamilyClass) 256)) ; >> 8
0))
;; font descriptor flags
(match-define (list FIXED_PITCH SERIF SYMBOLIC SCRIPT _UNUSED NONSYMBOLIC ITALIC)
(map (λ (x) (expt 2 x)) (range 7)))
(define flags (sum-flags
[(not (zero? (hash-ref (get-post-table font) 'isFixedPitch))) FIXED_PITCH]
[(<= 1 family-class 7) SERIF]
[#t SYMBOLIC] ; assume the font uses non-latin characters
[(= family-class 10) SCRIPT]
[(hash-ref (hash-ref (get-head-table font) 'macStyle) 'italic) ITALIC]))
;; generate a random tag (6 uppercase letters. 65 is the char code for 'A')
(when (test-mode) (random-seed 0))
(define tag (list->string (for/list ([i (in-range 6)])
(integer->char (random 65 (+ 65 26))))))
(define name (string->symbol (string-append tag "+" (font-postscript-name font))))
(define descriptor (make-ref
(mhasheq
'Type 'FontDescriptor
'FontName name
'Flags flags
'FontBBox (map (λ (x) (* scale x)) (bbox->list @bbox))
'ItalicAngle (font-italic-angle font)
'Ascent @ascender
'Descent @descender
'CapHeight (* (or (font-cap-height font) @ascender) scale)
'XHeight (* (or (font-x-height font) 0) scale)
'StemV 0)))
(dict-set! descriptor (if isCFF 'FontFile3 'FontFile2) font-file)
(ref-end descriptor)
(define descendant-font (make-ref
(mhasheq
'Type 'Font
'Subtype (if isCFF 'CIDFontType0 'CIDFontType2)
'BaseFont name
'CIDSystemInfo
(mhasheq
'Registry "Adobe"
'Ordering "Identity"
'Supplement 0)
'FontDescriptor descriptor
'W (list 0 (for/list ([idx (in-range (length (hash-keys widths)))])
(hash-ref widths idx (λ () (error 'embed (format "hash key ~a not found" idx)))))))))
(ref-end descendant-font)
(dict-set*! @ref
'Type 'Font
'Subtype 'Type0
'BaseFont name
'Encoding 'Identity-H
'DescendantFonts (list descendant-font)
'ToUnicode (to-unicode-cmap))
(ref-end @ref))
(define/public (to-unicode-cmap)
(define cmap-ref (make-ref))
(define entries
(for/list ([idx (in-range (length (hash-keys unicode)))])
(define codepoints (hash-ref unicode idx))
(define encoded ; encode codePoints to utf16
;; todo: full utf16 support. for now just utf8
(for/list ([value (in-list codepoints)])
(to-hex value)))
(format "<~a>" (string-join encoded " "))))
(define unicode-cmap-str #<<HERE
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo <<
/Registry (Adobe)
/Ordering (UCS)
/Supplement 0
>> def
/CMapName /Adobe-Identity-UCS def
/CMapType 2 def
1 begincodespacerange
<0000><ffff>
endcodespacerange
1 beginbfrange
<0000> <~a> [~a]
endbfrange
endcmap
CMapName currentdict /CMap defineresource pop
end
end
HERE
)
(ref-write cmap-ref (format unicode-cmap-str (to-hex (sub1 (length entries))) (string-join entries " ")))
(ref-end cmap-ref)
cmap-ref)))
(module+ test
(require rackunit fontland sugar/unstable/js)
(define ef (make-object embedded-font% "../ptest/assets/charter.ttf"))
(check-equal? (get-field ascender ef) 980)
(check-equal? (get-field descender ef) -238)
(check-equal? (get-field line-gap ef) 0)
(check-equal? (bbox->list (get-field bbox ef)) '(-161 -236 1193 963))
(define H-gid 41)
(check-equal? (get-field widths ef) (mhasheqv 0 278))
(check-equal? (send ef string-width "f" 1000) 321.0)
(check-equal? (glyph-advance-width (get-glyph (get-field font ef) H-gid)) 738))

@ -1,32 +0,0 @@
#lang racket/base
(require racket/class "reference.rkt")
(provide pdf-font%)
;; 181227 structifying the fonts didn't do anything for speed
;; the class is implementation is equally fast, and less code
(define pdf-font%
(class object%
(super-new)
(init-field [(@ascender ascender) #f]
[(@descender descender) #f]
[(@line-gap line-gap) #f]
[(@bbox bbox) #f])
(field [(@ref ref) #f]
[@embedded #f])
(abstract embed encode string-width)
(define/public (make-font-ref)
(unless @ref
(set! @ref (make-ref)))
@ref)
(define/public (font-end)
(unless (or @embedded (not @ref))
(embed)
(set! @embedded #t)))
(define/public (line-height size [include-gap #f])
(define gap (if include-gap @line-gap 0))
(* (/ (+ @ascender gap (- @descender)) 1000.0) size))))

@ -0,0 +1,215 @@
#lang debug racket/base
(require
"core.rkt"
"reference.rkt"
racket/class
racket/match
racket/string
racket/format
racket/list
racket/dict
sugar/unstable/dict
fontland)
(provide make-embedded-font)
#|
approximates
https://github.com/mbutterick/pdfkit/blob/master/lib/font/embedded.coffee
|#
(define width-cache (make-hash))
(define-syntax-rule (sum-flags [COND VAL] ...)
(for/sum ([c (in-list (list COND ...))]
[v (in-list (list VAL ...))]
#:when c)
v))
(define (to-hex . codepoints)
(string-append*
(for/list ([code (in-list codepoints)])
(~r code #:base 16 #:min-width 4 #:pad-string "0"))))
(struct efont pdf-font (font subset unicode widths scale encoding-cache) #:mutable)
(define (make-embedded-font name-arg [id #f])
(define font (cond
[(string? name-arg) (open-font name-arg)]
[(path? name-arg) (open-font (path->string name-arg))]))
(define subset (create-subset font))
;; we make `unicode` and `width` fields integer-keyed hashes not lists
;; because they offer better random access and growability
(define unicode (mhasheq 0 '(0))) ; always include the missing glyph (gid = 0)
(define widths (mhasheq 0 (glyph-advance-width (get-glyph font 0))))
(define name (font-postscript-name font))
(define scale (/ 1000 (font-units-per-em font)))
(define ascender (* (font-ascent font) scale))
(define descender (* (font-descent font) scale))
(define bbox (font-bbox font))
(define line-gap (* (font-linegap font) scale))
(define encoding-cache (make-hash)) ; needs to be per font, not in top level of module
(efont
name id ascender descender line-gap bbox #f #f efont-embedded efont-encode efont-measure-string
font subset unicode widths scale encoding-cache))
(define (efont-measure-string ef str size [features null])
; #f disables features ; null enables default features ; list adds features
(define scale (/ size (+ (font-units-per-em (efont-font ef)) 0.0)))
;; use `encode` because it's cached.
;; we assume that the side effects of `encode`
;; (e.g., appending to `widths` and `unicode`)
;; are ok because every string that gets measured is going to be encoded eventually
(match-define (list _ posns) (efont-encode ef str features))
(define width (for/sum ([p (in-vector posns)]) (glyph-position-x-advance p)))
(* width scale))
(define (efont-encode ef str [features-in null])
(define features (sort (remove-duplicates features-in) bytes<? #:key car))
(hash-ref! (efont-encoding-cache ef) (cons str features)
(λ ()
(define glyph-run (layout (efont-font ef) str #:features features))
(define glyphs (glyphrun-glyphs glyph-run))
(define positions (glyphrun-positions glyph-run))
(define len (vector-length glyphs))
(define subset-idxs (make-vector len))
(define new-positions (make-vector len))
(for ([glyph (in-vector glyphs)]
[posn (in-vector positions)]
[idx (in-range len)])
(define gid (subset-add-glyph! (efont-subset ef) (glyph-id glyph)))
(define subset-idx (to-hex gid))
(vector-set! subset-idxs idx subset-idx)
(set-glyph-position-advance-width! posn (glyph-advance-width glyph))
(scale-glyph-position! posn (efont-scale ef))
(vector-set! new-positions idx posn)
(hash-ref! (efont-widths ef) gid (λ () (glyph-position-advance-width posn)))
(hash-ref! (efont-unicode ef) gid (λ () (glyph-codepoints glyph))))
(list subset-idxs new-positions))))
(define (efont-embedded ef)
(define isCFF #false) ; no CFF support, but leave notations
(define font-file (make-ref))
(when isCFF
(dict-set! font-file 'Subtype 'CIDFontType0C))
(ref-write font-file (get-output-bytes (encode-to-port (efont-subset ef))))
(ref-end font-file)
(define family-class
(if (has-table? (efont-font ef) 'OS/2)
(floor (/ (hash-ref (get-OS/2-table (efont-font ef)) 'sFamilyClass) 256)) ; >> 8
0))
;; font descriptor flags
(match-define (list FIXED_PITCH SERIF SYMBOLIC SCRIPT _UNUSED NONSYMBOLIC ITALIC)
(map (λ (x) (expt 2 x)) (range 7)))
(define flags (sum-flags
[(not (zero? (hash-ref (get-post-table (efont-font ef)) 'isFixedPitch))) FIXED_PITCH]
[(<= 1 family-class 7) SERIF]
[#t SYMBOLIC] ; assume the font uses non-latin characters
[(= family-class 10) SCRIPT]
[(hash-ref (hash-ref (get-head-table (efont-font ef)) 'macStyle) 'italic) ITALIC]))
;; generate a random tag (6 uppercase letters. 65 is the char code for 'A')
(when (test-mode) (random-seed 0))
(define tag (list->string (for/list ([i (in-range 6)])
(integer->char (random 65 (+ 65 26))))))
(define name (string->symbol (string-append tag "+" (font-postscript-name (efont-font ef)))))
(define descriptor (make-ref
(mhasheq
'Type 'FontDescriptor
'FontName name
'Flags flags
'FontBBox (map (λ (x) (* (efont-scale ef) x)) (bbox->list (pdf-font-bbox ef)))
'ItalicAngle (font-italic-angle (efont-font ef))
'Ascent (pdf-font-ascender ef)
'Descent (pdf-font-descender ef)
'CapHeight (* (or (font-cap-height (efont-font ef)) (pdf-font-ascender ef)) (efont-scale ef))
'XHeight (* (or (font-x-height (efont-font ef)) 0) (efont-scale ef))
'StemV 0)))
(dict-set! descriptor (if isCFF 'FontFile3 'FontFile2) font-file)
(ref-end descriptor)
(define descendant-font (make-ref
(mhasheq
'Type 'Font
'Subtype (if isCFF 'CIDFontType0 'CIDFontType2)
'BaseFont name
'CIDSystemInfo
(mhasheq
'Registry "Adobe"
'Ordering "Identity"
'Supplement 0)
'FontDescriptor descriptor
'W (list 0 (for/list ([idx (in-range (length (hash-keys (efont-widths ef))))])
(hash-ref (efont-widths ef) idx (λ () (error 'embed (format "hash key ~a not found" idx)))))))))
(ref-end descendant-font)
(dict-set*! (pdf-font-ref ef)
'Type 'Font
'Subtype 'Type0
'BaseFont name
'Encoding 'Identity-H
'DescendantFonts (list descendant-font)
'ToUnicode (to-unicode-cmap ef))
(ref-end (pdf-font-ref ef)))
(define (to-unicode-cmap ef)
(define cmap-ref (make-ref))
(define entries
(for/list ([idx (in-range (length (hash-keys (efont-unicode ef))))])
(define codepoints (hash-ref (efont-unicode ef) idx))
(define encoded ; encode codePoints to utf16
;; todo: full utf16 support. for now just utf8
(for/list ([value (in-list codepoints)])
(to-hex value)))
(format "<~a>" (string-join encoded " "))))
(define unicode-cmap-str #<<HERE
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo <<
/Registry (Adobe)
/Ordering (UCS)
/Supplement 0
>> def
/CMapName /Adobe-Identity-UCS def
/CMapType 2 def
1 begincodespacerange
<0000><ffff>
endcodespacerange
1 beginbfrange
<0000> <~a> [~a]
endbfrange
endcmap
CMapName currentdict /CMap defineresource pop
end
end
HERE
)
(ref-write cmap-ref (format unicode-cmap-str (to-hex (sub1 (length entries))) (string-join entries " ")))
(ref-end cmap-ref)
cmap-ref)
(module+ test
(require rackunit fontland sugar/unstable/js)
(define ef (make-embedded-font "../ptest/assets/charter.ttf"))
(check-equal? (pdf-font-ascender ef) 980)
(check-equal? (pdf-font-descender ef) -238)
(check-equal? (pdf-font-line-gap ef) 0)
(check-equal? (bbox->list (pdf-font-bbox ef)) '(-161 -236 1193 963))
(define H-gid 41)
(check-equal? (efont-widths ef) (mhasheq 0 278))
(check-equal? (efont-measure-string ef "f" 1000) 321.0)
(check-equal? (glyph-advance-width (get-glyph (efont-font ef) H-gid)) 738))

@ -4,7 +4,6 @@
racket/string
racket/match
sugar/unstable/dict
"font-base.rkt"
"core.rkt"
"reference.rkt"
fontland
@ -12,77 +11,75 @@
racket/list
with-cache)
(provide standard-font-name? standard-font%)
(provide standard-font-name? make-standard-font)
(define-runtime-path here ".")
(define standard-font%
(class pdf-font%
(init-field name id)
(struct sfont pdf-font (attributes glyph-widths kern-pairs) #:transparent #:mutable)
(match-define (list atts gws kps) (parse-afm (open-input-file (build-path here (format "data/~a.afm" name)))))
(field [@attributes (make-hasheq atts)]
[@glyph-widths (make-hash gws)]
[@kern-pairs (make-hash kps)])
(define (make-standard-font name id)
(match-define (list atts gws kps) (parse-afm (open-input-file (build-path here (format "data/~a.afm" name)))))
(define attributes (make-hasheq atts))
(define glyph-widths (make-hash gws))
(define kern-pairs (make-hash kps))
(define ascender (string->number (hash-ref attributes 'Ascender "0")))
(define descender (string->number (hash-ref attributes 'Descender "0")))
(define bbox (for/list ([attr (in-list (string-split (hash-ref attributes 'FontBBox)))])
(or (string->number attr) 0)))
(define line-gap (- (third bbox) (first bbox) ascender descender))
(sfont
name id ascender descender line-gap bbox #f #f sfont-embed sfont-encode sfont-measure-string
attributes glyph-widths kern-pairs))
(let* ([ascender (string->number (hash-ref @attributes 'Ascender "0"))]
[descender (string->number (hash-ref @attributes 'Descender "0"))]
[bbox (for/list ([attr (in-list (string-split (hash-ref @attributes 'FontBBox)))])
(or (string->number attr) 0))]
[line-gap (- (third bbox) (first bbox) ascender descender)])
(super-new [ascender ascender] [descender descender] [bbox bbox] [line-gap line-gap]))
(define (sfont-embed sf)
(set-$ref-payload! (pdf-font-ref sf)
(mhash 'Type 'Font
'BaseFont (string->symbol (pdf-font-name sf))
'Subtype 'Type1
'Encoding 'WinAnsiEncoding))
(ref-end (pdf-font-ref sf)))
(inherit-field [@ref ref])
(define/override (embed)
(set-$ref-payload! @ref
(mhash 'Type 'Font
'BaseFont (string->symbol name)
'Subtype 'Type1
'Encoding 'WinAnsiEncoding))
(ref-end @ref))
(define/public (character-to-glyph char)
(define cint (char->integer char))
(define idx (hash-ref win-ansi-table cint cint))
(vector-ref characters (if (< idx (vector-length characters)) idx 0)))
(define (character-to-glyph char)
(define cint (char->integer char))
(define idx (hash-ref win-ansi-table cint cint))
(vector-ref characters (if (< idx (vector-length characters)) idx 0)))
(define/public (glyphs-for-string str)
(for/list ([c (in-string str)])
(character-to-glyph c)))
(define (glyphs-for-string str)
(for/list ([c (in-string str)])
(character-to-glyph c)))
(define/public (glyph-width glyph)
(hash-ref @glyph-widths glyph 0))
(define (glyph-width sf glyph)
(hash-ref (sfont-glyph-widths sf) glyph 0))
(define/public (advances-for-glyphs glyphs)
(for/list ([left (in-list glyphs)]
[right (in-list (append (cdr glyphs) (list #\nul)))])
(+ (glyph-width left) (get-kern-pair left right))))
(define (advances-for-glyphs sf glyphs)
(for/list ([left (in-list glyphs)]
[right (in-list (append (cdr glyphs) (list #\nul)))])
(+ (glyph-width sf left) (get-kern-pair sf left right))))
(define/public (get-kern-pair left right)
(hash-ref @kern-pairs (make-kern-table-key left right) 0))
(define (get-kern-pair sf left right)
(hash-ref (sfont-kern-pairs sf) (make-kern-table-key left right) 0))
(define encoding-cache (make-hash))
(define encoding-cache (make-hash))
(define/override (encode str [options #f])
(hash-ref encoding-cache str
(λ ()
(define encoded
(for/vector ([c (in-string str)])
(define cint (char->integer c))
(number->string (hash-ref win-ansi-table cint cint) 16)))
(define glyphs (glyphs-for-string str))
(define positions
(for/vector ([glyph (in-list glyphs)]
[advance (in-list (advances-for-glyphs glyphs))])
(+glyph-position advance 0 0 0 (glyph-width glyph))))
(list encoded positions))))
(define (sfont-encode sf str [options #f])
(hash-ref encoding-cache str
(λ ()
(define encoded
(for/vector ([c (in-string str)])
(define cint (char->integer c))
(number->string (hash-ref win-ansi-table cint cint) 16)))
(define glyphs (glyphs-for-string str))
(define positions
(for/vector ([glyph (in-list glyphs)]
[advance (in-list (advances-for-glyphs sf glyphs))])
(+glyph-position advance 0 0 0 (glyph-width sf glyph))))
(list encoded positions))))
(define/override (string-width str size [options #f])
(match-define (list _ posns) (encode str options))
(define width (for/sum ([p (in-vector posns)]) (glyph-position-x-advance p)))
(define scale (/ size 1000.0))
(* width scale))))
(define (sfont-measure-string sf str size [options #f])
(match-define (list _ posns) (sfont-encode sf str options))
(define width (for/sum ([p (in-vector posns)]) (glyph-position-x-advance p)))
(define scale (/ size 1000.0))
(* width scale))
(define standard-fonts
(map symbol->string '(Courier-Bold
@ -109,7 +106,7 @@
(check-true (standard-font-name? "ZapfDingbats"))
(check-false (standard-font-name? "Not A Font Name"))
(define stdfont (make-object standard-font% "Helvetica" #f)))
(define stdfont (make-standard-font "Helvetica" #f)))
(define (make-kern-table-key left right)

@ -4,15 +4,41 @@
racket/match
racket/class
racket/list
"standard-font.rkt"
"embedded-font.rkt")
"reference.rkt"
"font-standard.rkt"
"font-embedded.rkt")
(provide (all-defined-out))
(define (make-font-ref f)
(or (pdf-font-ref f)
(and (set-pdf-font-ref! f (make-ref)) (pdf-font-ref f))))
(define (embed f)
(define embed-proc (pdf-font-embed f))
(embed-proc f))
(define (encode f str [options #f])
(define encode-proc (pdf-font-encode f))
(encode-proc f str options))
(define (measure-string f str size [options #f])
(define measure-proc (pdf-font-measure-string f))
(measure-proc f str size options))
(define (font-end f)
(unless (or (pdf-font-embedded f) (not (pdf-font-ref f)))
(embed f)
(set-pdf-font-embedded! f #t)))
(define (line-height f size [include-gap #f])
(define gap (if include-gap (pdf-font-line-gap f) 0))
(* (/ (+ (pdf-font-ascender f) gap (- (pdf-font-descender f))) 1000.0) size))
(define (open-pdf-font name id)
(make-object (if (standard-font-name? name) standard-font% embedded-font%) name id))
((if (standard-font-name? name) make-standard-font make-embedded-font) name id))
(define (current-line-height doc [include-gap #f])
(send (pdf-current-font doc) line-height (pdf-current-font-size doc) include-gap))
(line-height (pdf-current-font doc) (pdf-current-font-size doc) include-gap))
(define (font doc src [size #f])
;; check registered fonts if src is a string
@ -34,11 +60,11 @@
(define id (string->symbol (format "F~a" font-index)))
(set-pdf-current-font! doc (open-pdf-font src id))
;; check for existing font families with the same name already in the PDF
(match (hash-ref (pdf-font-families doc) (get-field name (pdf-current-font doc)) #f)
(match (hash-ref (pdf-font-families doc) (pdf-font-name (pdf-current-font doc)) #f)
[(? values font) (set-pdf-current-font! doc font)]
[_ ;; save the font for reuse later
(when cache-key (hash-set! (pdf-font-families doc) cache-key (pdf-current-font doc)))
(hash-set! (pdf-font-families doc) (get-field name (pdf-current-font doc)) (pdf-current-font doc))])])
(hash-set! (pdf-font-families doc) (pdf-font-name (pdf-current-font doc)) (pdf-current-font doc))])])
doc)
(define (font-size doc size)

@ -107,8 +107,7 @@
(define doc-info (make-ref (pdf-info doc)))
(ref-end doc-info)
(for ([font (in-hash-values (pdf-font-families doc))])
(send font font-end))
(for-each font-end (hash-values (pdf-font-families doc)))
(define pages-ref (dict-ref (pdf-root doc) 'Pages))
(dict-set! pages-ref 'Count (length (pdf-pages doc)))

@ -63,12 +63,12 @@ https://github.com/mbutterick/pdfkit/blob/master/lib/mixins/text.coffee
(transform doc 1 0 0 -1 0 page-height)
(define y (- page-height
y-in
(* (/ (get-field ascender (pdf-current-font doc)) 1000)
(* (/ (pdf-font-ascender (pdf-current-font doc)) 1000)
(pdf-current-font-size doc))))
;; add current font to page if necessary
(define current-font-id (get-field id (pdf-current-font doc)))
(hash-ref! (page-fonts (current-page doc)) current-font-id (λ () (send (pdf-current-font doc) make-font-ref)))
(define current-font-id (pdf-font-id (pdf-current-font doc)))
(hash-ref! (page-fonts (current-page doc)) current-font-id (λ () (make-font-ref (pdf-current-font doc))))
(add-content doc "BT") ; begin the text object
(add-content doc (format "1 0 0 1 ~a ~a Tm" (numberizer x) (numberizer y))) ; text position
@ -82,7 +82,7 @@ https://github.com/mbutterick/pdfkit/blob/master/lib/mixins/text.coffee
;; Add the actual text
(match-define (list encoded-char-strs positions)
(send (pdf-current-font doc) encode text (hash-ref options 'features (pdf-current-font-features doc))))
(encode (pdf-current-font doc) text (hash-ref options 'features (pdf-current-font-features doc))))
(define scale (/ (pdf-current-font-size doc) 1000.0))
(define commands empty)
@ -139,5 +139,5 @@ https://github.com/mbutterick/pdfkit/blob/master/lib/mixins/text.coffee
(when (test-mode) (set-pdf-x! doc (+ (pdf-x doc) (string-width doc str)))))
(define (string-width doc str [options (mhash)])
(+ (send (pdf-current-font doc) string-width str (pdf-current-font-size doc) (hash-ref options 'features (pdf-current-font-features doc)))
(+ (measure-string (pdf-current-font doc) str (pdf-current-font-size doc) (hash-ref options 'features (pdf-current-font-features doc)))
(* (hash-ref options 'characterSpacing 0) (sub1 (string-length str)))))

Loading…
Cancel
Save