Merge remote-tracking branch 'fontland/master'

main
Matthew Butterick 2 years ago
commit eb8cc8b303

@ -0,0 +1,35 @@
name: CI
on: [push, pull_request]
jobs:
run:
name: "Build using Racket '${{ matrix.racket-version }}' (${{ matrix.racket-variant }})"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
racket-version: ["7.1", "7.2", "7.3", "7.4", "7.5", "7.6", "7.7", "7.8", "7.9", "current"]
racket-variant: ["BC", "CS"]
# CS builds are only provided for versions 7.4 and up so avoid
# running the job for prior versions.
exclude:
- {racket-version: "7.1", racket-variant: "CS"}
- {racket-version: "7.2", racket-variant: "CS"}
- {racket-version: "7.3", racket-variant: "CS"}
steps:
- name: Checkout
uses: actions/checkout@master
- uses: Bogdanp/setup-racket@v0.11
with:
distribution: 'full'
version: ${{ matrix.racket-version }}
variant: ${{ matrix.racket-variant }}
- name: Install package and its dependencies
run: raco pkg install --auto --batch
- name: Run the tests
run: xvfb-run raco test -j 4 -p fontland

@ -0,0 +1,21 @@
# for Racket
compiled/
*~
# for Mac OS X
.DS_Store
.AppleDouble
.LSOverride
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
doc/*
scribblings/*.css
scribblings/*.js
scribblings/*.html
fontland/*.sqlite

@ -0,0 +1,9 @@
MIT License for `fontland`
© 2017-2019 Matthew Butterick
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,16 @@
## fontland ![Build Status](https://github.com/mbutterick/fontland/workflows/CI/badge.svg)
Racket library for messing with fonts. Based on [`fontkit`](https://github.com/foliojs/fontkit).
## License
MIT (code)
Fonts in `assets` folder are under separate licenses included there.
## Project status
Actively developed, but this package exists entirely to support [`pitfall`](https://github.com/mbutterick/pitfall) and [`quad`](https://github.com/mbutterick/quad). I have no ambition to make this a standalone library with documentation. I add things opportunistically based on whats happening with Quad.

@ -0,0 +1,47 @@
Fontland contains substantial portions of the following software:
[fontkit](https://github.com/devongovett/fontkit)
MIT LICENSE
Copyright (c) 2014 Devon Govett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
[racket-fontconfig](https://github.com/takikawa/racket-fontconfig)
Copyright (c) 2016 Asumu Takikawa
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1 @@
This is a copy of the Charter fonts which Bitstream contributed to the X consortium, arranged for use in the MacOS. Here is the copyright notice: (c) Copyright 1989-1992, Bitstream Inc., Cambridge, MA. You are hereby granted permission under all Bitstream propriety rights to use, copy, modify, sublicense, sell, and redistribute the 4 Bitstream Charter (r) Type 1 outline fonts and the 4 Courier Type 1 outline fonts for any purpose and without restriction; provided, that this notice is left intact on all copies of such fonts and that Bitstream's trademark is acknowledged as shown below on all unmodified copies of the 4 Charter Type 1 fonts. BITSTREAM CHARTER is a registered trademark of Bitstream Inc.

@ -0,0 +1,94 @@
Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
with Reserved Font Name < Fira >,
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -0,0 +1,92 @@
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

@ -0,0 +1,32 @@
((3)
0
()
0
()
()
(h
!
()
(tag u . "\u0000\u0001\u0000\u0000")
(rangeShift . 96)
(searchRange . 128)
(numTables . 14)
(tables
h
!
(equal)
(loca h ! () (tag u . "loca") (offset . 38692) (checkSum . 2795817194) (length . 460))
(OS/2 h ! () (tag u . "OS/2") (offset . 360) (checkSum . 2351070438) (length . 96))
(glyf h ! () (tag u . "glyf") (offset . 4620) (checkSum . 1143629849) (length . 34072))
(hhea h ! () (tag u . "hhea") (offset . 292) (checkSum . 132056097) (length . 36))
(post h ! () (tag u . "post") (offset . 41520) (checkSum . 1670855689) (length . 514))
(cvt_ h ! () (tag u . "cvt ") (offset . 4592) (checkSum . 10290865) (length . 26))
(VDMX h ! () (tag u . "VDMX") (offset . 1372) (checkSum . 1887795202) (length . 1504))
(prep h ! () (tag u . "prep") (offset . 4512) (checkSum . 490862356) (length . 78))
(maxp h ! () (tag u . "maxp") (offset . 328) (checkSum . 50135594) (length . 32))
(hmtx h ! () (tag u . "hmtx") (offset . 456) (checkSum . 3982043058) (length . 916))
(cmap h ! () (tag u . "cmap") (offset . 2876) (checkSum . 1723761408) (length . 1262))
(name h ! () (tag u . "name") (offset . 39152) (checkSum . 2629707307) (length . 2367))
(head h ! () (tag u . "head") (offset . 236) (checkSum . 4281190895) (length . 54))
(fpgm h ! () (tag u . "fpgm") (offset . 4140) (checkSum . 106535991) (length . 371)))
(entrySelector . 3)))

@ -0,0 +1,32 @@
((3)
0
()
0
()
()
(h
!
()
(tag u . "\u0000\u0001\u0000\u0000")
(rangeShift . 96)
(searchRange . 128)
(numTables . 14)
(tables
h
!
(equal)
(loca h ! () (tag u . "loca") (offset . 37392) (checkSum . 46801904) (length . 460))
(OS/2 h ! () (tag u . "OS/2") (offset . 360) (checkSum . 2367847603) (length . 96))
(glyf h ! () (tag u . "glyf") (offset . 4620) (checkSum . 2099535230) (length . 32772))
(hhea h ! () (tag u . "hhea") (offset . 292) (checkSum . 113838023) (length . 36))
(post h ! () (tag u . "post") (offset . 40280) (checkSum . 1671576585) (length . 514))
(cvt_ h ! () (tag u . "cvt ") (offset . 4592) (checkSum . 9307818) (length . 26))
(VDMX h ! () (tag u . "VDMX") (offset . 1372) (checkSum . 1905948947) (length . 1504))
(prep h ! () (tag u . "prep") (offset . 4512) (checkSum . 776081685) (length . 78))
(maxp h ! () (tag u . "maxp") (offset . 328) (checkSum . 50135583) (length . 32))
(hmtx h ! () (tag u . "hmtx") (offset . 456) (checkSum . 3798537071) (length . 916))
(cmap h ! () (tag u . "cmap") (offset . 2876) (checkSum . 1723761408) (length . 1262))
(name h ! () (tag u . "name") (offset . 37852) (checkSum . 2313429994) (length . 2427))
(head h ! () (tag u . "head") (offset . 236) (checkSum . 4275817075) (length . 54))
(fpgm h ! () (tag u . "fpgm") (offset . 4140) (checkSum . 106535991) (length . 371)))
(entrySelector . 3)))

@ -0,0 +1,114 @@
((3)
0
()
0
()
()
(h
!
()
(tag . OTTO)
(rangeShift . 64)
(searchRange . 128)
(numTables . 12)
(entrySelector . 3)
(tables
h
!
(equal)
(maxp
h
!
()
(tag . maxp)
(length . 6)
(checkSum . 172445696)
(offset . 204))
(CFF_
h
!
()
(tag . |CFF |)
(length . 164604)
(checkSum . 516494622)
(offset . 33472))
(hhea
h
!
()
(tag . hhea)
(length . 36)
(checkSum . 101714507)
(offset . 10792))
(post
h
!
()
(tag . post)
(length . 32)
(checkSum . 4290248754)
(offset . 33440))
(GDEF
h
!
()
(tag . GDEF)
(length . 392)
(checkSum . 936918765)
(offset . 198076))
(head
h
!
()
(tag . head)
(length . 54)
(checkSum . 95220183)
(offset . 212))
(GPOS
h
!
()
(tag . GPOS)
(length . 78818)
(checkSum . 4027394566)
(offset . 198468))
(cmap
h
!
()
(tag . cmap)
(length . 20808)
(checkSum . 3634078005)
(offset . 12632))
(name
h
!
()
(tag . name)
(length . 1706)
(checkSum . 2142217964)
(offset . 10924))
(GSUB
h
!
()
(tag . GSUB)
(length . 19078)
(checkSum . 1205824848)
(offset . 277288))
(hmtx
h
!
()
(tag . hmtx)
(length . 10524)
(checkSum . 2729095083)
(offset . 268))
(OS/2
h
!
()
(tag . OS/2)
(length . 96)
(checkSum . 1618270015)
(offset . 10828)))))

Binary file not shown.

Binary file not shown.

@ -0,0 +1,44 @@
#lang racket/base
(require racket/struct)
(provide (all-defined-out))
(struct bbox (min-x min-y max-x max-y) #:transparent #:mutable)
(define (+bbox [min-x +inf.0] [min-y +inf.0] [max-x -inf.0] [max-y -inf.0])
(bbox min-x min-y max-x max-y))
(define (bbox-width bb)
(unless (bbox? bb)
(raise-argument-error 'bbox-width "bbox" bb))
(- (bbox-max-x bb) (bbox-min-x bb)))
(define (bbox-height bb)
(unless (bbox? bb)
(raise-argument-error 'bbox-height "bbox" bb))
(- (bbox-max-y bb) (bbox-min-y bb)))
(define (bbox-add-point! bb x y)
(unless (bbox? bb)
(raise-argument-error 'bbox-add-point "bbox" bb))
(set-bbox-min-x! bb (min x (bbox-min-x bb)))
(set-bbox-min-y! bb (min y (bbox-min-y bb)))
(set-bbox-max-x! bb (max x (bbox-max-x bb)))
(set-bbox-max-y! bb (max y (bbox-max-y bb)))
(void))
(define (bbox-copy bb)
(unless (bbox? bb)
(raise-argument-error 'bbox-copy "bbox" bb))
(apply +bbox (bbox->list bb)))
(define bbox->list struct->list)
(module+ test
(require rackunit)
(define bb (+bbox 1 2 4 8))
(check-equal? (bbox-width bb) 3)
(check-equal? (bbox-height bb) 6)
(bbox-add-point! bb 0 0)
(check-equal? (bbox-width bb) 4)
(check-equal? (bbox-height bb) 8)
(check-equal? (bbox->list (bbox-copy bb)) (bbox->list bb)))

@ -0,0 +1,424 @@
#lang debug racket
(require racket/match
xenomorph
fontland/struct
fontland/table-stream
fontland/table/cff/cff-font
fontland/path
"deque.rkt")
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/glyph/CFFGlyph.js
|#
(define (bias this s)
(cond
[(< (vector-length s) 1240) 107]
[(< (vector-length s) 33900) 1131]
[else 32768]))
(define-syntax-rule (case= ID [(NUMS ...) . BODY] ... [else . ELSEBODY])
(cond
[(memq ID (list NUMS ...)) . BODY] ...
[else . ELSEBODY]))
(define (getPath this)
(define cff (get-table (glyph-font this) 'CFF_))
(define str (vector-ref (hash-ref* cff 'topDict 'CharStrings) (glyph-id this)))
(define end (+ (index-item-offset str) (index-item-length str)))
(define stream (get-table-stream (glyph-font this) 'CFF_))
(pos stream (index-item-offset str))
(define path (Path))
#|
(define (shift deque) (pop-start! deque))
(define (push deque . vals) (apply push-end! deque vals))
(define (pop deque) (pop-end! deque))
(define initialize-stack make-deque)
(define stack-length deque-length)
|#
(define-syntax-rule (shift ID)
(begin0
(car ID)
(set! ID (cdr ID))))
(define-syntax-rule (push ID VAL ...)
(begin
(set! ID (append ID (list VAL ...)))
ID))
(define-syntax-rule (pop ID)
(cond
[(> (length ID) 0)
(define-values (head last) (split-at-right ID 1))
(set! ID head)
(car last)]))
(define (initialize-stack) null)
(define stack-length length)
(define stack (initialize-stack))
(define trans null)
(define width #false)
(define nStems 0)
(define x 0)
(define y 0)
(define used-gsubrs (make-hasheq))
(define used-subrs (make-hasheq))
(define open #false)
(define gsubrs (hash-ref cff 'globalSubrIndex (vector)))
(define gsubrs-bias (bias this gsubrs))
(define privateDict (or (private-dict-for-glyph cff (glyph-id this)) (make-hasheq)))
(define subrs (hash-ref privateDict 'Subrs (vector)))
(define subrs-bias (bias this subrs))
(define (check-width)
(unless width
(set! width (+ (shift stack) (hash-ref privateDict 'nominalWidthX)))))
(define (parse-stems)
(when (odd? (stack-length stack))
(check-width))
(set! nStems (+ nStems (arithmetic-shift (stack-length stack) -1)))
(set! stack (initialize-stack))
0)
(define (moveTo x y)
(when open (path-closePath path))
(path-moveTo path x y)
(set! open #true))
(let parse ()
(let/ec return
(for ([i (in-naturals)]
#:break (>= (pos stream) end))
(define op (read-byte stream))
(cond
[(< op 32)
(case= op
[(1 ;; hstem
3 ;; vstem
18 ;; hstemhm
23) ;; vstemhm
(parse-stems)]
[(4) ;; vmoveto
(when (> (stack-length stack) 1)
(check-width))
(set! y (+ y (shift stack)))
(moveTo x y)]
[(5) ;; rlineto
(let loop ()
(when (>= (stack-length stack) 2)
(set! x (+ x (shift stack)))
(set! y (+ y (shift stack)))
(path-lineTo path x y)
(loop)))]
[(6 ;; hlineto
7) ;; vlineto
(let loop ([phase (= op 6)])
(when (>= (stack-length stack) 1)
(if phase
(set! x (+ x (shift stack)))
(set! y (+ y (shift stack))))
(path-lineTo path x y)
(loop (not phase))))]
[(8) ;; rrcurveto
(let loop ()
(when (positive? (stack-length stack))
(define c1x (+ x (shift stack)))
(define c1y (+ y (shift stack)))
(define c2x (+ c1x (shift stack)))
(define c2y (+ c1y (shift stack)))
(set! x (+ c2x (shift stack)))
(set! y (+ c2y (shift stack)))
(path-bezierCurveTo path c1x c1y c2x c2y x y)
(loop)))]
[(10) ;; callsubr
(define index (+ (pop stack) subrs-bias))
(define subr (vector-ref subrs index))
(when subr
(hash-set! used-subrs index #true)
(define p (pos stream))
(define e end)
(pos stream (index-item-offset subr))
(set! end (+ (index-item-offset subr) (index-item-length subr)))
(parse)
(pos stream p)
(set! end e))]
[(11) ;; return
(when (< (hash-ref cff 'version) 2)
(return))]
[(14) ;; endchar
(when (< (hash-ref cff 'version) 2)
(when (> (stack-length stack) 0)
(check-width))
(when open
(path-closePath path)
(set! open #false)))]
[(15) ;; vsindex
(when (< (hash-ref cff 'version) 2)
(error 'vsindex-operator-not-supported))]
[(16) ;; blend
(error 'blend-operator-not-supported)]
[(19 ;; hintmask
20) ;; cntrmask
(parse-stems)
(pos stream (+ (pos stream) (arithmetic-shift (+ nStems 7) -3)))]
[(21) ;; rmoveto
(when (> (stack-length stack) 2)
(check-width))
(set! x (+ x (shift stack)))
(set! y (+ y (shift stack)))
(moveTo x y)]
[(22) ;; hmoveto
(when (> (stack-length stack) 1)
(check-width))
(set! x (+ x (shift stack)))
(moveTo x y)]
[(24) ;; rcurveline
(let loop ()
(when (>= (stack-length stack) 8)
(define c1x (+ x (shift stack)))
(define c1y (+ y (shift stack)))
(define c2x (+ c1x (shift stack)))
(define c2y (+ c1y (shift stack)))
(set! x (+ c2x (shift stack)))
(set! y (+ c2y (shift stack)))
(path-bezierCurveTo path c1x c1y c2x c2y x y)
(loop)))
(set! x (+ x (shift stack)))
(set! y (+ y (shift stack)))
(path-lineTo path x y)]
[(25) ;; rlinecurve
(let loop ()
(when (>= (stack-length stack) 8)
(set! x (+ x (shift stack)))
(set! y (+ y (shift stack)))
(path-lineTo path x y)
(loop)))
(define c1x (+ x (shift stack)))
(define c1y (+ y (shift stack)))
(define c2x (+ c1x (shift stack)))
(define c2y (+ c1y (shift stack)))
(set! x (+ c2x (shift stack)))
(set! y (+ c2y (shift stack)))
(path-bezierCurveTo path c1x c1y c2x c2y x y)]
[(26) ;; vvcurveto
(when (odd? (stack-length stack))
(set! x (+ x (shift stack))))
(let loop ()
(when (>= (stack-length stack) 4)
(define c1x x)
(define c1y (+ y (shift stack)))
(define c2x (+ c1x (shift stack)))
(define c2y (+ c1y (shift stack)))
(set! x c2x)
(set! y (+ c2y (shift stack)))
(path-bezierCurveTo path c1x c1y c2x c2y x y)
(loop)))]
[(27) ;; hhcurveto
(when (odd? (stack-length stack))
(set! y (+ y (shift stack))))
(let loop ()
(when (>= (stack-length stack) 4)
(define c1x (+ x (shift stack)))
(define c1y y)
(define c2x (+ c1x (shift stack)))
(define c2y (+ c1y (shift stack)))
(set! x (+ c2x (shift stack)))
(set! y c2y)
(path-bezierCurveTo path c1x c1y c2x c2y x y)
(loop)))]
[(28) ;; shortint
(push stack (decode int16be stream))]
[(29) ;; callgsubr
(define index (+ (pop stack) gsubrs-bias))
(define subr (vector-ref gsubrs index))
(when subr
(hash-set! used-gsubrs index #true)
(define old-pos (pos stream))
(define old-end end)
(pos stream (index-item-offset subr))
(set! end (+ (index-item-offset subr) (index-item-length subr)))
(parse)
(pos stream old-pos)
(set! end old-end))]
[(30 ;; vhcurveto
31) ;; hvcurveto
(let loop ([phase (= op 31)])
(when (>= (stack-length stack) 4)
(cond
[phase
(define c1x (+ x (shift stack)))
(define c1y y)
(define c2x (+ c1x (shift stack)))
(define c2y (+ c1y (shift stack)))
(set! y (+ c2y (shift stack)))
(set! x (+ c2x (if (= (stack-length stack) 1) (shift stack) 0)))
(path-bezierCurveTo path c1x c1y c2x c2y x y)
(loop (not phase))]
[else
(define c1x x)
(define c1y (+ y (shift stack)))
(define c2x (+ c1x (shift stack)))
(define c2y (+ c1y (shift stack)))
(set! x (+ c2x (shift stack)))
(set! y (+ c2y (if (= (stack-length stack) 1) (shift stack) 0)))
(path-bezierCurveTo path c1x c1y c2x c2y x y)
(loop (not phase))])))]
[(12)
(println "warning: check truthiness")
(set! op (read-byte stream))
(case= op
[(3) ;; and
(push stack (if (and (pop stack) (pop stack)) 1 0))]
[(4) ;; or
(push stack (if (or (pop stack) (pop stack)) 1 0))]
[(5) ;; not
(push stack (if (pop stack) 0 1))]
[(9) ;; abs
(push stack (abs (pop stack)))]
[(10) ;; add
(push stack (+ (pop stack) (pop stack)))]
[(11) ;; sub
(push stack (- (pop stack) (pop stack)))]
[(12) ;; div
(push stack (/ (pop stack) (pop stack) 1.0))]
[(14) ;; neg
(push stack (- (pop stack)))]
[(15) ;; eq
(push stack (if (- (pop stack) (pop stack)) 1 0))]
[(18) ;; drop
(pop stack)]
[(20) ;; put
(define val (pop stack))
(define idx (pop stack))
(set! trans (list-set trans idx val))]
[(21) ;; get
(push stack (or (list-ref trans (pop stack)) 0))]
[(22) ;; ifelse
(define s1 (pop stack))
(define s2 (pop stack))
(define v1 (pop stack))
(define v2 (pop stack))
(push stack (if (<= v1 v2) s1 s2))]
[(23) ;; random
(push stack (random))]
[(24) ;; mul
(push stack (* (pop stack) (pop stack)))]
[(26) ;; sqrt
(push stack (sqrt (pop stack)))]
[(26) ;; dup
(define a (pop stack))
(push stack a a)]
[(28) ;; exch
(define a (pop stack))
(define b (pop stack))
(push stack b a)]
[(29) ;; index
(define idx
(min (max 0 (pop stack)) (- (stack-length stack) 1)))
(push stack (list-ref stack idx))]
[(30) ;; roll
(define n (pop stack))
(define j (pop stack))
(cond
[(>= j 0)
(let loop ([j j])
(when (positive? j)
(define t (list-ref stack (- n 1)))
(for [(i (in-range (- n 2) (sub1 0) -1))]
(set! stack (list-set stack (+ i 1) (list-ref stack i))))
(set! stack (list-set stack 0 t))
(loop (sub1 j))))]
[else
(let loop ([j j])
(when (negative? j)
(define t (list-ref stack 0))
(for ([i (in-range (add1 n))])
(set! stack (list-set stack i (list-ref stack (+ i 1)))))
(set! stack (list-set stack (- n 1) t))
(loop (add1 j))))])]
[(34) ;; hflex
(define c1x (+ x (shift stack)))
(define c1y y)
(define c2x (+ c1x (shift stack)))
(define c2y (+ c1y (shift stack)))
(define c3x (+ c2x (shift stack)))
(define c3y c2y)
(define c4x (+ c3x (shift stack)))
(define c4y c3y)
(define c5x (+ c4x (shift stack)))
(define c5y c4y)
(define c6x (+ c5x (shift stack)))
(define c6y c5y)
(set! x c6x)
(set! y c6y)
(path-bezierCurveTo path c1x c1y c2x c2y c3x c3y)
(path-bezierCurveTo path c4x c4y c5x c5y c6x c6y)]
[(35) ;; flex
(define pts null)
(for ([i (in-range (add1 5))])
(set! x (+ x (shift stack)))
(set! y (+ y (shift stack)))
(push pts x y))
(apply path-bezierCurveTo path (take pts 6))
(apply path-bezierCurveTo path (drop pts 6))
(shift stack)] ;; fd
[(36) ;; hflex1
(define c1x (+ x (shift stack)))
(define c1y (+ y (shift stack)))
(define c2x (+ c1x (shift stack)))
(define c2y (+ c1y (shift stack)))
(define c3x (+ c2x (shift stack)))
(define c3y c2y)
(define c4x (+ c3x (shift stack)))
(define c4y c3y)
(define c5x (+ c4x (shift stack)))
(define c5y (+ c4y (shift stack)))
(define c6x (+ c5x (shift stack)))
(define c6y c5y)
(set! x c6x)
(set! y c6y)
(path-bezierCurveTo path c1x c1y c2x c2y c3x c3y)
(path-bezierCurveTo path c4x c4y c5x c5y c6x c6y)]
[(37) ;; flex1
(define startx x)
(define starty y)
(define pts null)
(for ([i (in-range 0 (add1 4))])
(set! x (+ x (shift stack)))
(set! y (+ y (shift stack)))
(push pts x y))
(cond
[(> (abs (- x startx)) (abs (- y starty))) ;; horzontal
(set! x (shift stack))
(set! y starty)]
[else
(set! x startx)
(set! y (shift stack))])
(push pts x y)
(apply path-bezierCurveTo path (take pts 6))
(apply path-bezierCurveTo path (drop pts 6))]
[else (error (format "unknown op: 12 ~a" op))])]
[else (error (format "unknown op: ~a" op))])]
[else
(push stack (cond
[(< op 247) (- op 139)]
[(< op 251) (+ (* (- op 247) 256) (read-byte stream) 108)]
[(< op 255) (- (* (- 251 op) 256) (read-byte stream) 108)]
[else (/ (decode int32be stream) 65536.0)]))]))))
(when open (path-closePath path))
(set-cff-glyph-_usedSubrs! this used-subrs)
(set-cff-glyph-_usedGsubrs! this used-gsubrs)
(set-cff-glyph-path! this path)
path)

@ -0,0 +1,36 @@
#lang debug racket
(require sugar/cache racket/match
db racket/runtime-path)
(provide (all-defined-out))
(define-runtime-path db-file "fontland.sqlite")
(define current-query-debug (make-parameter #f))
(define current-dbc (make-parameter (sqlite3-connect #:database db-file #:mode 'create)))
(define-logger db)
(define (log-query q) (when (current-query-debug) (log-db-info q)))
(define-syntax-rule (query-exec-logging q arg ...)
(begin (log-query q) (query-exec (current-dbc) q arg ...)))
(define-syntax-rule (query-rows-logging q arg ...)
(begin (log-query q) (query-rows (current-dbc) q arg ...)))
(define (add-record! rec)
(query-exec-logging "insert or replace into layouts (crc, layout) values ($1, $2)" (car rec) (cdr rec)))
(define (get-layout-from-db which)
(match (query-rows-logging "select layout from layouts where crc==$1" which)
[(list (vector val)) val]
[_ #false]))
(define (init-db)
(query-exec-logging "create table if not exists layouts (crc INTEGER UNIQUE, layout BLOB)")
(query-exec-logging "create index if not exists layout_crcs ON layouts (crc);
"))
(module+ main
(init-db)
(add-record! (cons 42 (bytes 0 1 0 2 3 4)))
(get-layout-from-db 42))

@ -0,0 +1,122 @@
#lang debug racket/base
(require racket/struct racket/match)
(provide (all-defined-out))
(struct deque (start length) #:mutable
#:methods gen:custom-write
[(define write-proc
(make-constructor-style-printer
(λ (d) 'deque)
(λ (d) (deque->list d))))])
(struct deque-item (val prev next) #:mutable)
(define (before! di1 di2)
(set-deque-item-next! di1 di2)
(set-deque-item-prev! di2 di1))
(define (insert-before! new-di di)
((deque-item-prev di) . before! . new-di)
(new-di . before! . di))
(define (remove! di)
((deque-item-prev di) . before! . (deque-item-next di)))
(define (push-end! d . vals)
(apply push-start! d #:end #true vals))
(define (push-start! d #:end [end? #f] . vals)
(define-values (val-count first-di)
(for/fold ([count 0]
[first-di #f])
([val (in-list vals)])
(define di (deque-item val #f #f))
(match (deque-start d)
[#false (set-deque-start! d di)
(before! di di)]
[start (di . insert-before! . start)])
(values (add1 count) (or first-di di))))
(unless (zero? val-count)
(unless end?
(set-deque-start! d first-di))
(set-deque-length! d (+ val-count (deque-length d)))))
(define (pop-start! d #:end [end? #f])
(unless (zero? (deque-length d))
(define popdi ((if end? deque-item-prev values) (deque-start d)))
(begin0
(deque-item-val popdi)
(set-deque-start! d (and (> (deque-length d) 1) (deque-item-next popdi)))
(remove! popdi)
(set-deque-length! d (sub1 (deque-length d))))))
(define (pop-end! d)
(pop-start! d #:end #true))
(define (make-deque . vals)
(define d (deque #f 0))
(apply push-end! d vals)
d)
(define (deque-ref d idx)
(unless (< idx (deque-length d))
(error 'deque-idx-too-large))
(for/fold ([di (deque-start d)]
#:result (deque-item-val di))
([i (in-range idx)])
(deque-item-next di)))
(define (deque-rotate! d [count 0])
(unless (or (zero? count) (< (deque-length d) 2))
(cond
[(< (abs count) (deque-length d))
(define opp-count ((if (positive? count) - +) (- (deque-length d) (abs count))))
(define new-count (if (< (abs opp-count) (abs count)) opp-count count))
(define dir (if (positive? new-count) deque-item-next deque-item-prev))
(for/fold ([di (deque-start d)]
#:result (set-deque-start! d di))
([i (in-range (abs new-count))])
(dir di))]
[else (deque-rotate! d (modulo count ((if (positive? count) + -) (deque-length d))))])))
(define (deque->list d)
(for/fold ([vals null]
[di (deque-start d)]
#:result (reverse vals))
([i (in-range (deque-length d))])
(values (cons (deque-item-val di) vals) (deque-item-next di))))
(define (list->deque xs)
(apply make-deque xs))
(module+ test
(require rackunit)
(define d (make-deque 42))
(check-equal? (deque-length d) 1)
(push-end! d 43 44 45)
(check-equal? (deque-length d) 4)
(check-equal? (deque-ref d 0) 42)
(check-equal? (deque-ref d 1) 43)
(check-equal? (deque-ref d 2) 44)
(check-equal? (deque-ref d 3) 45)
(push-start! d 39 40 41)
(check-equal? (deque-length d) 7)
(check-equal? (deque-ref d 0) 39)
(check-equal? (deque-ref d 1) 40)
(check-equal? (deque-ref d 2) 41)
(check-equal? (deque-ref d 3) 42)
(check-equal? (pop-start! d) 39)
(check-equal? (pop-start! d) 40)
(check-equal? (pop-end! d) 45)
(check-equal? (pop-end! d) 44)
(check-equal? (deque-length d) 3)
(check-equal? (deque->list d) '(41 42 43))
(deque-rotate! d 1)
(check-equal? (deque->list d) '(42 43 41))
(deque-rotate! d -1)
(check-equal? (deque->list d) '(41 42 43))
(deque-rotate! d (deque-length d))
(check-equal? (deque->list d) '(41 42 43))
(let ([d (make-deque 90)])
(pop-start! d)
(push-end! d -1)
(check-equal? (deque->list d) (list -1))))

@ -0,0 +1,79 @@
#lang racket/base
(require xenomorph
"tables.rkt"
racket/dict
racket/class
racket/match
sugar/unstable/dict
racket/string)
(provide (all-defined-out))
#|
https://github.com/mbutterick/fontkit/blob/master/src/tables/directory.js
|#
(define table-entry (x:struct
'tag (x:symbol #:length 4)
'checkSum uint32be
'offset (x:pointer #:type uint32be
#:dest-type 'void
#:relative-to 'global)
'length uint32be))
;; for stupid tags like 'cvt '
(define (symbol-replace sym this that)
(string->symbol (string-replace (if (string? sym) sym (symbol->string sym)) this that)))
(define (escape-tag tag) (symbol-replace tag " " "_"))
(define (unescape-tag tag) (symbol-replace tag "_" " "))
(define (directory-post-decode this-res)
(define new-tables-val (mhash))
(for ([table (in-list (hash-ref this-res 'tables))])
(hash-set! new-tables-val (escape-tag (hash-ref table 'tag)) table))
(dict-set! this-res 'tables new-tables-val)
this-res)
(define (directory-pre-encode this-val)
(define tables (for/list ([tag-table-pair (in-list (hash-ref this-val 'tables))])
(match-define (cons tag table) tag-table-pair)
(define table-codec (hash-ref table-codecs tag))
(mhash 'tag (unescape-tag tag)
'checkSum 0
'offset (x:void-pointer table-codec table)
'length (send table-codec x:size table))))
(define numTables (length tables))
;; patch from https://github.com/foliojs/fontkit/pull/178
(define max-exponent-for-2 (floor (log numTables 2)))
(define searchRange (* (expt 2 max-exponent-for-2) 16))
(hash-set*! this-val
'tag 'true
'numTables numTables
'tables tables
'searchRange searchRange
'entrySelector max-exponent-for-2
'rangeShift (- (* numTables 16) searchRange))
this-val)
(define directory (x:struct #:pre-encode directory-pre-encode
#:post-decode directory-post-decode
'tag (x:symbol #:length 4)
'numTables uint16be
'searchRange uint16be
'entrySelector uint16be
'rangeShift uint16be
'tables (x:array #:type table-entry #:length (λ (p) (hash-ref p 'numTables)))))
(define (directory-decode ip [options (mhash)])
(decode directory ip))
(define (file-directory-decode ps)
(directory-decode (open-input-file ps)))
#;(module+ test
(require rackunit "helper.rkt" racket/serialize racket/file racket/pretty)
(define ip (open-input-file fira-otf-path))
(define dir (serialize (directory-decode ip)))
(pretty-write dir)
(with-output-to-file "assets/fira-otf-directory.rktd"
(λ () (pretty-write dir)) #:exists 'replace))

@ -0,0 +1,66 @@
#lang debug racket/base
(require racket/promise
racket/string
"unsafe/fontconfig.rkt")
(provide (all-defined-out))
(define fontsets-promise
(delay
(define font-dirs
(case (system-type 'os)
[(macosx)
;; https://support.apple.com/en-us/HT201722
(list "/System/Library/Fonts"
"/Library/Fonts"
(expand-user-path "~/Library/Fonts"))]
;; https://wiki.ubuntu.com/Fonts#Manually
[(unix) (list "/usr/share/fonts"
"/usr/local/share/fonts"
(expand-user-path "~/.fonts"))]
[else ;; windows
;; https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
;; on my windows 10 VM, the 'sys-dir is C:\\Windows\system32
;; though I'm suspicious that it's always like this
(list (build-path (find-system-path 'sys-dir) 'up "fonts"))]))
(define (path->fontset path-or-path-string)
(define bytepath (path->bytes (if (string? path-or-path-string)
(string->path path-or-path-string)
path-or-path-string)))
((cond
[(fc-file-is-dir bytepath) fc-dir-scan]
[else fc-file-scan]) bytepath))
(map path->fontset font-dirs)))
(define (probably-successful-match? family-name result)
;; fontconfig does its best to find a match using fuzzy logic
;; so there's no official failure condition, it seems.
;; this test checks if the first letters (up to 6) of the family name
;; appear somewhere in the font filename.
(define-values (dir name _) (split-path result))
(define name-string (path->string name))
(regexp-match (string-downcase (substring family-name 0 (min 6 (string-length family-name)))) (string-downcase name-string)))
;; this function follows c sample
;; https://gist.github.com/CallumDev/7c66b3f9cf7a876ef75f
(define (family->path family-name #:bold [bold? #f] #:italic [italic? #f])
;; create a configuration & invoke it
(fc-config-set-current (fc-config-create))
(define fontsets (force fontsets-promise))
(cond
[(ormap values fontsets)
;; query pattern syntax
;; https://www.freedesktop.org/software/fontconfig/fontconfig-user.html#AEN36
(define query-pattern
(fc-name-parse (string->bytes/utf-8 (format "~a:weight=~a:slant=~a" family-name (if bold? 200 80) (if italic? 100 0)))))
(fc-config-substitute query-pattern 'FcMatchPattern)
;; https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcdefaultsubstitute.html
;; Supplies default values for underspecified font patterns
(fc-default-substitute query-pattern)
(define result-pattern (fc-font-set-match fontsets query-pattern))
(define result
(and result-pattern (bytes->path (fc-pattern-get-string result-pattern #"file" 0))))
(cond
[(and result (probably-successful-match? family-name result)) result]
[else #false])]
[else #false]))

@ -0,0 +1,220 @@
#lang debug racket/base
(require "helper.rkt"
"unsafe/freetype.rkt"
"glyph.rkt"
"bbox.rkt"
"glyphrun.rkt"
"directory.rkt"
"woff-directory.rkt"
"woff2-directory.rkt"
"struct.rkt"
"table-stream.rkt"
"font-path.rkt"
xenomorph
racket/match
racket/list
racket/string
sugar/unstable/dict
"unsafe/harfbuzz.rkt"
"glyph-position.rkt"
sugar/list
racket/promise)
(provide (all-defined-out))
(define ft-library (delay (FT_Init_FreeType)))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/TTFFont.js
|#
(struct probe-fail exn ())
(define (+ttf-font port
#:directory [directory-class directory]
#:probe [probe-vals (list #"true" #"OTTO" (bytes 0 1 0 0))]
#:constructor [structor ttf-font])
(unless (input-port? port)
(raise-argument-error '+ttf-font "input port" port))
(unless (member (peek-bytes 4 0 port) probe-vals)
(raise (probe-fail "fail" (current-continuation-marks))))
(define decoded-tables (mhash))
(define src (path->string (object-name port)))
(define directory (delay (decode directory-class port #:parent (mhash x:start-offset-key 0))))
(define ft-face (delay (and src (FT_New_Face (force ft-library) src))))
(define hb-font (delay (and src (hb_ft_font_create (force ft-face)))))
(define hb-buf (delay (hb_buffer_create)))
(define crc (equal-hash-code port))
(define get-head-table-proc #f)
(define font
(structor port decoded-tables src directory ft-face hb-font hb-buf crc get-head-table-proc))
;; needed for `loca` table decoding cross-reference
(set-ttf-font-get-head-table-proc! font (delay (get-head-table font)))
font)
(define (font-postscript-name font) (FT_Get_Postscript_Name (ft-face font)))
(define (font-units-per-em font) (hash-ref (get-head-table font) 'unitsPerEm))
(define (font-ascent font) (hash-ref (get-hhea-table font) 'ascent))
(define (font-descent font) (hash-ref (get-hhea-table font) 'descent))
(define (font-linegap font) (hash-ref (get-hhea-table font) 'lineGap))
(define (font-underline-position font) (hash-ref (get-post-table font) 'underlinePosition))
(define (font-underline-thickness font) (hash-ref (get-post-table font) 'underlineThickness))
(define (font-italic-angle font) (hash-ref (get-post-table font) 'italicAngle))
(define (font-cap-height font)
(if (has-table? font #"OS/2")
(hash-ref (get-OS/2-table font) 'capHeight)
(font-ascent font)))
(define (font-x-height font)
(if (has-table? font #"OS/2")
(hash-ref (get-OS/2-table font) 'xHeight)
0))
(define (font-bbox font)
(define head-table (get-head-table font))
(+bbox (hash-ref head-table 'xMin) (hash-ref head-table 'yMin)
(hash-ref head-table 'xMax) (hash-ref head-table 'yMax)))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/WOFFFont.js
|#
(define (+woff-font port)
(+ttf-font port
#:directory woff-directory
#:probe (list #"wOFF")
#:constructor woff-font))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/WOFF2Font.js
|#
(define (+woff2-font port)
(+ttf-font port
#:directory woff2-directory
#:probe (list #"wOF2")
#:constructor woff-font))
;; 181228: disk-based caching (either with sqlite or `with-cache`) is a loser
;; reads & writes aren't worth it vs. recomputing
;; (though this is good news, as it avoids massive disk caches hanging around)
;; ram cache in pitfall suffices
(define (layout font str
#:features [features null]
#:script [script #f]
#:language [lang #f]
#:direction [direction #f])
(match (for/list ([c (in-string str)]) (char->integer c))
[(? null?) (glyphrun (vector) (vector))]
[codepoints
(define buf (hb-buf font))
(hb_buffer_reset buf)
(when script
(hb_buffer_set_script buf script))
(when lang
(hb_buffer_set_language buf (hb_language_from_string lang)))
(when direction
(hb_buffer_set_direction buf direction))
(hb_buffer_add_codepoints buf codepoints)
(hb_shape (hb-font font) buf (map (λ (fpr) (tag->hb-feature (car fpr) (cdr fpr))) features))
(define gis (hb_buffer_get_glyph_infos buf))
(define hb-gids (map hb_glyph_info_t-codepoint gis))
;; `remove-duplicates` in case we get a funny codepoint that expands into multiple glyphs (rare but possible)
(define hb-clusters (break-at codepoints (remove-duplicates (map hb_glyph_info_t-cluster gis) eq?)))
(define hb-positions (map hb_glyph_position_t->list (hb_buffer_get_glyph_positions buf)))
(define glyphs (for/vector ([gidx (in-list hb-gids)]
[cluster (in-list hb-clusters)])
(get-glyph font gidx cluster)))
(define positions (for/vector ([posn (in-list hb-positions)])
(apply +glyph-position posn)))
(glyphrun glyphs positions)]))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/index.js
|#
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/base.js
|#
#;(define (query-fontconfig fam [bold #f] [italic #f])
#|
https://www.freedesktop.org/software/fontconfig/fontconfig-user.html
Fontconfig provides a textual representation for patterns that the library can both accept and generate. The representation is in three parts, first a list of family names, second a list of point sizes and finally a list of additional properties:
<families>-<point sizes>:<name1>=<values1>:<name2>=<values2>...
|#
(define pat-str (string-join (filter values
(list fam
(and bold "bold")
(and italic "italic"))) ":"))
(define fp (fc-name-parse (string->bytes/utf-8 pat-str)))
(fc-config-substitute (fc-config-get-current) fp 'FcMatchPattern)
(fc-default-substitute fp)
(define-values (res pat) (fc-font-match #f fp))
(for/hasheq ([str (cdr (string-split (bytes->string/utf-8 (fc-name-unparse pat)) ":"))])
(let loop ([kv (string-split str "=")])
(match kv
[(list k) (loop (list k "True"))]
[(list k v) (values (string->symbol k)
(match v
["True" #t]
["False" #f]
[(? string->number) (string->number v)]
[val val]))]))))
(define (open-font str-or-path #:bold [bold #f] #:italic [italic #f])
;; rather than use a `probe` function,
;; just try making a font with each format and see what happens
(define str (if (path? str-or-path) (path->string str-or-path) str-or-path))
(or
;; wrapping these in thunks so that they are not evaluated until the iteration demands it
(for*/or ([path-string-thunk (in-list (list (λ () str)
(λ () (family->path str #:bold bold #:italic italic))))]
[path-string (in-value (path-string-thunk))]
#:when (and path-string (file-exists? path-string))
[port (in-value (open-input-file path-string))]
[font-constructor (in-list (list +ttf-font +woff-font))])
(with-handlers ([probe-fail? (λ (exn) #f)])
(font-constructor port)))
(raise-argument-error 'open-font "valid font" str-or-path)))
(module+ test
(require rackunit racket/struct racket/vector)
(define charter (open-font charter-path))
(define charter-woff (open-font charter-woff-path))
(define fira (open-font (path->string fira-path)))
(define otf (open-font (path->string fira-otf-path)))
(for ([charter (list charter charter-woff)])
(check-equal? (font-postscript-name charter) "Charter")
(check-equal? (font-units-per-em charter) 1000)
(check-equal? (font-ascent charter) 980)
(check-equal? (font-descent charter) -238)
(check-equal? (font-linegap charter) 0)
(check-equal? (font-underline-thickness charter) 58)
(check-equal? (font-italic-angle charter) 0)
(check-equal? (font-cap-height charter) 671)
(check-equal? (font-x-height charter) 481)
(check-equal? (bbox->list (font-bbox charter)) '(-161 -236 1193 963))
(check-equal? (glyph-position-x-advance (vector-ref (glyphrun-positions (layout charter "f")) 0)) 321)
(check-true (has-table? charter #"cmap"))
(check-exn exn:fail:contract? (λ () (get-table charter 'nonexistent-table-tag))))
(check-true
(let ([gr (layout fira "Rifle")])
(and (equal? (vector-map glyph-id (glyphrun-glyphs gr)) '#(227 480 732 412))
(equal? (vector-map struct->list (glyphrun-positions gr)) '#((601 0 0 0 0) (279 0 0 0 0) (580 0 0 0 0) (547 0 0 0 0)))))))

@ -0,0 +1,26 @@
#lang racket/base
(provide (all-defined-out))
;; Represents positioning information for a glyph in a GlyphRun.
;; x-advance = The amount to move the virtual pen in the X direction after rendering this glyph.
;; y-advance = The amount to move the virtual pen in the Y direction after rendering this glyph.
;; x-offset = The offset from the pen position in the X direction at which to render this glyph.
;; y-offset = The offset from the pen position in the Y direction at which to render this glyph.
(struct glyph-position (x-advance y-advance x-offset y-offset advance-width) #:transparent #:mutable)
(define (+glyph-position [x-advance 0] [y-advance 0] [x-offset 0] [y-offset 0] [advance-width 0])
(glyph-position x-advance y-advance x-offset y-offset advance-width))
(define (scale-glyph-position! gp factor)
(set-glyph-position-x-advance! gp (* factor (glyph-position-x-advance gp)))
(set-glyph-position-y-advance! gp (* factor (glyph-position-y-advance gp)))
(set-glyph-position-x-offset! gp (* factor (glyph-position-x-offset gp)))
(set-glyph-position-y-offset! gp (* factor (glyph-position-y-offset gp)))
(set-glyph-position-advance-width! gp (* factor (glyph-position-advance-width gp)))
gp)
(module+ test
(require rackunit)
(define gp (+glyph-position 1 2 3 4))
(check-true (glyph-position? gp))
(check-equal? (scale-glyph-position! gp 2) (+glyph-position 2 4 6 8 0)))

@ -0,0 +1,67 @@
#lang debug racket/base
(require sugar/unstable/dict
"unsafe/freetype.rkt"
"table-stream.rkt"
"struct.rkt"
"helper.rkt")
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/glyph/Glyph.js
|#
; Glyph objects represent a glyph in the font. They have various properties for accessing metrics and
; the actual vector path the glyph represents, and methods for rendering the glyph to a graphics context.
;
; You do not create glyph objects directly. They are created by various methods on the font object.
; There are several subclasses of the base Glyph class internally that may be returned depending
; on the font format, but they all inherit from this class.
(define (is-mark? codepoint)
;; mark classes = Mn Me Mc
(regexp-match #px"\\p{Mn}|\\p{Me}|\\p{Mc}" (string (integer->char codepoint))))
(define (+glyph id codepoints font
[is-mark? (andmap is-mark? codepoints)]
[is-ligature? (> (length codepoints) 1)]
[metrics #f]
#:constructor [constructor glyph])
(constructor id codepoints font is-mark? is-ligature? metrics))
(define (glyph-advance-width g)
(hash-ref (get-glyph-metrics g) 'advanceWidth))
(define (get-glyph-metrics g)
(unless (glyph-metrics g)
(define face (ft-face (glyph-font g)))
(FT_Load_Glyph face (glyph-id g))
(define glyph (FT_FaceRec-glyph face))
(define ft-glyph-metrics (FT_GlyphSlotRec-metrics glyph))
(set-glyph-metrics! g (mhash))
;; todo: get vertical metrics
(hash-set*! (glyph-metrics g)
'advanceWidth (FT_Glyph_Metrics-horiAdvance ft-glyph-metrics)
'leftBearing (FT_Glyph_Metrics-horiBearingX ft-glyph-metrics)
'advanceHeight 'unfinished
'topBearing 'unfinished))
(glyph-metrics g))
(define (+ttf-glyph . args)
(apply +glyph #:constructor ttf-glyph args))
(define (+cff-glyph . args)
(apply +glyph #:constructor make-cff-glyph args))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/glyph/TTFFont.js
|#
;; Returns a glyph object for the given glyph id.
;; You can pass the array of code points this glyph represents for
;; your use later, and it will be stored in the glyph object.
(define (get-glyph font gid [codepoints null])
((if (has-table? font #"CFF_")
+cff-glyph
+ttf-glyph) gid codepoints font))

@ -0,0 +1,37 @@
#lang racket/base
(require "glyph-position.rkt")
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/layout/GlyphRun.js
|#
;; Represents a run of Glyph and GlyphPosition objects.
;; Returned by the font layout method.
; An array of Glyph objects in the run
; An array of GlyphPosition objects for each glyph in the run
(struct glyphrun (glyphs positions) #:transparent)
(define (+glyphrun [glyphs null] [positions null])
(glyphrun glyphs positions))
(define (glyphrun-advance-width gr)
(for/sum ([pos (in-list (glyphrun-positions gr))])
(glyph-position-x-advance pos)))
(define (append-glyphruns . grs)
(for/fold ([glyphss null]
[positionss null]
#:result (glyphrun
(apply append (reverse glyphss))
(apply append (reverse positionss))))
([gr (in-list grs)])
(values (cons (glyphrun-glyphs gr) glyphss)
(cons (glyphrun-positions gr) positionss))))
(module+ test
(require rackunit)
(define gr (+glyphrun))
(check-true (glyphrun? gr))
(check-equal? (append-glyphruns gr gr) gr))

@ -0,0 +1,17 @@
#lang racket
(require racket/runtime-path)
(provide (all-defined-out))
(define-syntax-rule (r+p ID ...)
(begin (require ID ...) (provide (all-from-out ID ...))))
(define index? (λ (x) (and (number? x) (integer? x) (not (negative? x)))))
(define-runtime-path charter-path "assets/charter.ttf")
(define-runtime-path charter-woff-path "assets/charter.woff")
(define-runtime-path charter-italic-path "assets/charter-italic.ttf")
(define-runtime-path fira-path "assets/fira.ttf")
(define-runtime-path fira-otf-path "assets/fira.otf")
(define-runtime-path charter-directory-path "assets/charter-directory.rktd")
(define-runtime-path charter-italic-directory-path "assets/charter-italic-directory.rktd")
(define-runtime-path fira-otf-directory-path "assets/fira-otf-directory.rktd")

@ -0,0 +1,16 @@
#lang racket/base
(define-syntax-rule (r+p ID ...)
(begin (require ID ...) (provide (all-from-out ID ...))))
(r+p "font.rkt"
"glyph-position.rkt"
"subset.rkt"
"bbox.rkt"
"tables.rkt"
"glyphrun.rkt"
"glyph.rkt"
"table-stream.rkt"
"subset.rkt"
"struct.rkt"
"zlib.rkt")

@ -0,0 +1,59 @@
#lang debug racket
(require sugar/unstable/dict
(for-syntax racket/syntax))
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/glyph/Path.js
|#
#|
/**
* Path objects are returned by glyphs and represent the actual
* vector outlines for each glyph in the font. Paths can be converted
* to SVG path data strings, or to functions that can be applied to
* render the path to a graphics context.
*/
|#
(struct Path$ (commands _bbox _cbox) #:transparent #:mutable)
(define (Path [commands null] [_bbox #false] [_cbox #false])
(Path$ commands _bbox _cbox))
(define SVG_COMMANDS (hasheq 'moveTo "M"
'lineTo "L"
'quadraticCurveTo "Q"
'bezierCurveTo "C"
'closePath "Z"))
(define (toSVG this)
(define cmds (for/list ([c (in-list (Path$-commands this))])
(define args (for/list ([arg (in-list (dict-ref c 'args))])
(round (/ (* arg 100) 100))))
(format "~a~a" (hash-ref SVG_COMMANDS (dict-ref c 'command))
(string-join (map ~a args) " "))))
(string-join cmds ""))
(define-syntax (define-command stx)
(syntax-case stx ()
[(_ COMMAND)
(with-syntax [(ID (format-id #'COMMAND (format "path-~a" (syntax-e #'COMMAND))))]
#'(define (ID this . args)
(set-Path$-_bbox! this #false)
(set-Path$-_cbox! this #false)
(set-Path$-commands! this
(append
(Path$-commands this)
(list
(dictify
'command 'COMMAND
'args args))))
this))]))
(define-command moveTo)
(define-command lineTo)
(define-command quadraticCurveTo)
(define-command bezierCurveTo)
(define-command closePath)

@ -0,0 +1,186 @@
#lang racket/base
(require sugar/unstable/dict
sugar/unstable/contract
sugar/unstable/stub
racket/contract)
(provide (all-defined-out))
#|
https://github.com/mbutterick/fontkit/blob/master/src/layout/Script.js
|#
;; This maps the Unicode Script property to an OpenType script tag
;; Data from http://www.microsoft.com/typography/otspec/scripttags.htm
;; and http://www.unicode.org/Public/UNIDATA/PropertyValueAliases.txt.
(define UNICODE_SCRIPTS
(apply mhash
'(Caucasian_Albanian aghb
Arabic arab
Imperial_Aramaic armi
Armenian armn
Avestan avst
Balinese bali
Bamum bamu
Bassa_Vah bass
Batak batk
Bengali '(bng2 beng)
Bopomofo bopo
Brahmi brah
Braille brai
Buginese bugi
Buhid buhd
Chakma cakm
Canadian_Aboriginal cans
Carian cari
Cham cham
Cherokee cher
Coptic copt
Cypriot cprt
Cyrillic cyrl
Devanagari '(dev2 deva)
Deseret dsrt
Duployan dupl
Egyptian_Hieroglyphs egyp
Elbasan elba
Ethiopic ethi
Georgian geor
Glagolitic glag
Gothic goth
Grantha gran
Greek grek
Gujarati '(gjr2 gujr)
Gurmukhi '(gur2 guru)
Hangul hang
Han hani
Hanunoo hano
Hebrew hebr
Hiragana hira
Pahawh_Hmong hmng
Katakana_Or_Hiragana hrkt
Old_Italic ital
Javanese java
Kayah_Li kali
Katakana kana
Kharoshthi khar
Khmer khmr
Khojki khoj
Kannada '(knd2 knda)
Kaithi kthi
Tai_Tham lana
Lao lao
Latin latn
Lepcha lepc
Limbu limb
Linear_A lina
Linear_B linb
Lisu lisu
Lycian lyci
Lydian lydi
Mahajani mahj
Mandaic mand
Manichaean mani
Mende_Kikakui mend
Meroitic_Cursive merc
Meroitic_Hieroglyphs mero
Malayalam '(mlm2 mlym)
Modi modi
Mongolian mong
Mro mroo
Meetei_Mayek mtei
Myanmar '(mym2 mymr)
Old_North_Arabian narb
Nabataean nbat
Nko nko
Ogham ogam
Ol_Chiki olck
Old_Turkic orkh
Oriya orya
Osmanya osma
Palmyrene palm
Pau_Cin_Hau pauc
Old_Permic perm
Phags_Pa phag
Inscriptional_Pahlavi phli
Psalter_Pahlavi phlp
Phoenician phnx
Miao plrd
Inscriptional_Parthian prti
Rejang rjng
Runic runr
Samaritan samr
Old_South_Arabian sarb
Saurashtra saur
Shavian shaw
Sharada shrd
Siddham sidd
Khudawadi sind
Sinhala sinh
Sora_Sompeng sora
Sundanese sund
Syloti_Nagri sylo
Syriac syrc
Tagbanwa tagb
Takri takr
Tai_Le tale
New_Tai_Lue talu
Tamil taml
Tai_Viet tavt
Telugu '(tel2 telu)
Tifinagh tfng
Tagalog tglg
Thaana thaa
Thai thai
Tibetan tibt
Tirhuta tirh
Ugaritic ugar
Vai vai
Warang_Citi wara
Old_Persian xpeo
Cuneiform xsux
Yi yi
Inherited zinh
Common zyyy
Unknown zzzz)))
(define/contract (fromUnicode script)
((option/c symbol?) . -> . symbol?)
(hash-ref UNICODE_SCRIPTS script #f))
(define-stub-stop forString)
(define-stub-stop forCodePoints)
(define RTL '( arab ;; Arabic
hebr ;; Hebrew
syrc ;; Syriac
thaa ;; Thaana
cprt ;; Cypriot Syllabary
khar ;; Kharosthi
phnx ;; Phoenician
|nko | ;; N'Ko
lydi ;; Lydian
avst ;; Avestan
armi ;; Imperial Aramaic
phli ;; Inscriptional Pahlavi
prti ;; Inscriptional Parthian
sarb ;; Old South Arabian
orkh ;; Old Turkic, Orkhon Runic
samr ;; Samaritan
mand ;; Mandaic, Mandaean
merc ;; Meroitic Cursive
mero ;; Meroitic Hieroglyphs
;; Unicode 7.0 (not listed on http://www.microsoft.com/typography/otspec/scripttags.htm)
mani ;; Manichaean
mend ;; Mende Kikakui
nbat ;; Nabataean
narb ;; Old North Arabian
palm ;; Palmyrene
phlp ;; Psalter Pahlavi
))
(define (direction script)
(if (memq script RTL) 'rtl 'ltr))

@ -0,0 +1,49 @@
#lang debug racket
(provide (all-defined-out))
(struct ttf-font (port decoded-tables src directory ft-face hb-font hb-buf crc get-head-table-proc)
#:mutable
#:property prop:custom-write
(λ (f p w?) (display
(format "<~a ~a>"
(object-name f)
(let-values ([(dir name _) (split-path (ttf-font-src f))])
name)) p)))
(struct woff-font ttf-font ())
(define (ft-face this)
(or (force (ttf-font-ft-face this)) (error 'ft-face-not-available)))
(define (font-directory this)
(or (force (ttf-font-directory this)) (error 'directory-not-available)))
(define (hb-font this)
(or (force (ttf-font-hb-font this)) (error 'hb-font-not-available)))
(define (hb-buf this)
(or (force (ttf-font-hb-buf this)) (error 'hp-buf-not-available)))
(struct glyph (id codepoints font is-mark? is-ligature? metrics) #:mutable
#:property prop:custom-write
(λ (g p w?) (display
(format "<~a ~a ~a>"
(object-name g)
(glyph-id g)
(glyph-font g)) p)))
(struct ttf-glyph glyph ())
; glyphs = list of glyph ids in the subset
; mapping = of glyph ids to indexes in glyphs
(struct subset (font glyphs mapping) #:mutable)
(struct cff-subset subset (cff strings charstrings gsubrs) #:mutable)
(struct cff-glyph glyph (path _usedGsubrs _usedSubrs) #:mutable)
(define (make-cff-glyph . args)
(apply cff-glyph (append args (list #f (make-hash) (make-hash)))))
(struct index-item (offset length) #:transparent #:mutable)

@ -0,0 +1,307 @@
#lang debug racket/base
(require racket/serialize
racket/list
racket/match
sugar/unstable/dict
"table/loca.rkt"
"table-stream.rkt"
"directory.rkt"
"struct.rkt"
fontland/glyph
fontland/ttf-glyph
fontland/cff-glyph
xenomorph
racket/dict
fontland/table/cff/cff-font
fontland/table/cff/cff-top
fontland/table/cff/cff-standard-strings)
(provide subset +subset
ttf-subset +ttf-subset ttf-subset?
cff-subset +cff-subset cff-subset?
subset-add-glyph! encode-to-port create-subset)
#|
from
https://github.com/mbutterick/fontkit/blob/master/src/TTFFont.js
|#
(define (create-subset font)
((if (has-table? font #"CFF_")
+cff-subset
+ttf-subset) font))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/subset/Subset.js
|#
(define (+subset font [glyphs empty] [mapping (mhash)])
(define ss (subset font glyphs mapping))
(subset-add-glyph! ss 0)
ss)
(define (encode-to-port ss)
(define p (open-output-bytes))
((if (cff-subset? ss)
cff-subset-encode
ttf-subset-encode) ss p)
p)
(define (subset-add-glyph! ss glyph-or-gid) ; fka `includeGlyph`
(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
(λ ()
(set-subset-glyphs! ss (append (subset-glyphs ss) (list new-gid)))
(sub1 (length (subset-glyphs ss))))))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/subset/CFFSubset.js
|#
(define (+cff-subset font [glyphs empty] [mapping (mhash)]
[cff (get-table font 'CFF_)]
[strings #f]
[charstrings #f]
[gsubrs #f])
(define ss (cff-subset font glyphs mapping cff strings charstrings gsubrs))
(subset-add-glyph! ss 0)
ss)
(require racket/format racket/string)
(define (bytes->hexes bs)
(string-join
(for/list ([b (in-bytes bs)])
(~r #:base 16 b #:min-width 2 #:pad-string "0"))  " "))
(define (subset-charstrings this)
(set-cff-subset-charstrings! this null)
(define gsubrs (make-hasheq))
(for ([gid (in-list (subset-glyphs this))])
(set-cff-subset-charstrings!
this
(append (cff-subset-charstrings this)
(list (get-char-string (cff-subset-cff this) gid))))
(define glyph (get-glyph (subset-font this) gid))
(unless (cff-glyph-path glyph) (getPath glyph)) ;; this causes the glyph to be parsed
(for ([subr (in-hash-keys (cff-glyph-_usedGsubrs glyph))])
(hash-set! gsubrs subr #true)))
(set-cff-subset-gsubrs! this (subset-subrs
this
(hash-ref (cff-subset-cff this) 'globalSubrIndex)
gsubrs)))
(define (subset-subrs this subrs used)
(for/vector ([(subr i) (in-indexed subrs)])
(cond
[(hash-ref used i #false)
(pos (hash-ref (cff-subset-cff this) 'stream) (index-item-offset subr))
(read-bytes (index-item-length subr) (hash-ref (cff-subset-cff this) 'stream))]
[else (bytes 11)])))
(define (subset-font-dict this topDict)
(error 'subsetFontdict-unimplemented))
(define (create-cid-fontdict this top-dict)
(define used-subrs (make-hasheq))
(for ([gid (in-list (subset-glyphs this))])
(define glyph (get-glyph (subset-font this) gid))
(unless (cff-glyph-path glyph) (getPath glyph)) ;; this causes the glyph to be parsed
(for ([subr (in-hash-keys (cff-glyph-_usedSubrs glyph))])
(hash-set! used-subrs subr #true)))
(define cff-topDict (hash-ref (cff-subset-cff this) 'topDict))
(define private-dict (hash-copy (hash-ref cff-topDict 'Private (make-hasheq))))
(when (and (hash-has-key? cff-topDict 'Private) (hash-has-key? (hash-ref cff-topDict 'Private) 'Subrs))
(hash-set! private-dict 'Subrs (subset-subrs this
(hash-ref (hash-ref cff-topDict 'Private) 'Subrs)
used-subrs)))
(hash-set! top-dict 'FDArray (list (dictify 'Private private-dict)))
(hash-set! top-dict 'FDSelect (dictify 'version 3
'nRanges 1
'ranges (list (dictify 'first 0 'fd 0))
'sentinel (length (cff-subset-charstrings this))))
(hash-ref top-dict 'FDSelect))
(define (add-string this [string #f])
(cond
[(not string) #false]
[else
(unless (cff-subset-strings this)
(set-cff-subset-strings! this null))
(set-cff-subset-strings! this
(append (cff-subset-strings this) (list string)))
(+ (vector-length standard-strings) (sub1 (length (cff-subset-strings this))))]))
(define (cff-subset-encode this stream)
(subset-charstrings this)
(define charset
(dictify 'version (if (> (length (cff-subset-charstrings this)) 255) 2 1)
'ranges (list (dictify 'first 1 'nLeft (- (length (cff-subset-charstrings this)) 2)))))
(define top-dict (hash-copy (hash-ref (cff-subset-cff this) 'topDict)))
(hash-set*! top-dict
'Private #false
'charset charset
'Encoding #false
'CharStrings (cff-subset-charstrings this))
(for ([key (in-list '(version Notice Copyright FullName
FamilyName Weight PostScript
BaseFontName FontName))])
(hash-update! top-dict key
(λ (tdk-val) (add-string this (CFFont-string (cff-subset-cff this) tdk-val)))))
(hash-set! top-dict 'ROS (list (add-string this "Adobe") (add-string this "Identity") 0))
(hash-set! top-dict 'CIDCount (length (cff-subset-charstrings this)))
(if (hash-ref (cff-subset-cff this) 'isCIDFont)
(subset-font-dict this top-dict)
(create-cid-fontdict this top-dict))
(define top
(mhasheq 'version 1
'hdrSize (hash-ref (cff-subset-cff this) 'hdrSize)
'offSize 4
'header (hash-ref (cff-subset-cff this) 'header #f)
'nameIndex (list (CFFFont-postscriptName (cff-subset-cff this)))
'topDictIndex (list top-dict)
'stringIndex (cff-subset-strings this)
'globalSubrIndex (cff-subset-gsubrs this)))
(for ([k (in-list (sort (dict-keys top-dict) symbol<?))])
(match (dict-ref top-dict k)
[(or (? list? (? dict?))) k]
[val val]))
(encode CFFTop top stream))
#;(module+ test
(require "font.rkt" "helper.rkt")
(define otf (open-font (path->string fira-otf-path)))
(define cffss (+cff-subset otf))
cffss)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/subset/TTFSubset.js
|#
(struct ttf-subset subset (glyf offset loca hmtx) #:transparent #:mutable)
(define (+ttf-subset font [glyphs empty] [mapping (mhash)]
[glyf #f]
[offset #f]
[loca #f]
[hmtx #f])
(define ss (ttf-subset font glyphs mapping glyf offset loca hmtx))
(subset-add-glyph! ss 0)
ss)
(define (ttf-subset-add-glyph ss gid)
;; glyph-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 glyph-decode.
(define glyph (get-glyph (subset-font ss) gid))
(define ttf-glyf-data (glyph-decode glyph))
;; get the offset to the glyph from the loca table
(match-define (list this-offset next-offset)
(take (drop (hash-ref (get-table (subset-font ss) 'loca) 'offsets) gid) 2))
(define port (get-table-stream (subset-font ss) 'glyf))
(pos port (+ (pos port) this-offset))
(define glyf-bytes (read-bytes (- next-offset this-offset) port))
;; if it is a compound glyph, include its components
(when (and ttf-glyf-data (negative? (hash-ref ttf-glyf-data 'numberOfContours)))
(for ([ttf-glyph-component (in-list (hash-ref ttf-glyf-data 'components))])
(define gid (subset-add-glyph! ss (ttf-glyph-component-glyph-id ttf-glyph-component)))
;; note: this (ttf-glyph-component-pos component) is correct. It's a field of a Component object, not a port
(bytes-copy! glyf-bytes (ttf-glyph-component-pos ttf-glyph-component) (encode uint16be gid #f))))
;; `loca` table v0 stores offsets as half of actual value
;; so we need an even number of bytes to encode
(define glyf-bytes-even (if (odd? (bytes-length glyf-bytes))
(bytes-append glyf-bytes #"0")
glyf-bytes))
(set-ttf-subset-glyf! ss (append (ttf-subset-glyf ss) (list glyf-bytes-even)))
(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 (hash-ref (get-glyph-metrics glyph) 'leftBearing))))))
(set-ttf-subset-offset! ss (+ (ttf-subset-offset ss) (bytes-length glyf-bytes-even)))
(sub1 (length (ttf-subset-glyf ss))))
;; 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
(define (clone-deep val) (deserialize (serialize val)))
(define (ttf-subset-encode ss port)
(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))
;; 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 (subset-glyphs ss))))
(define gid (list-ref (subset-glyphs ss) idx))
(ttf-subset-add-glyph ss gid))
(define new-maxp-table (clone-deep (get-maxp-table (subset-font ss))))
(dict-set! new-maxp-table 'numGlyphs (length (ttf-subset-glyf ss)))
;; populate the new loca table
(dict-update! (ttf-subset-loca ss) 'offsets (λ (vals) (append vals (list (ttf-subset-offset ss)))))
(loca-pre-encode (ttf-subset-loca ss))
(define new-head-table (clone-deep (get-head-table (subset-font ss))))
(dict-set! new-head-table 'indexToLocFormat (dict-ref (ttf-subset-loca ss) x:version-key))
(define new-hhea-table (clone-deep (get-hhea-table (subset-font ss))))
(dict-set! new-hhea-table 'numberOfMetrics (length (dict-ref (ttf-subset-hmtx ss) 'metrics)))
(define new-tables
(filter cdr (dictify 'head new-head-table
'hhea new-hhea-table
'loca (ttf-subset-loca ss)
'maxp new-maxp-table
'cvt_ (and (has-table? (subset-font ss) 'cvt_)
(get-cvt_-table (subset-font ss)))
'prep (and (has-table? (subset-font ss) 'prep)
(get-prep-table (subset-font ss)))
'glyf (ttf-subset-glyf ss)
'hmtx (ttf-subset-hmtx ss)
'fpgm (and (has-table? (subset-font ss) 'fpgm)
(get-fpgm-table (subset-font ss))))))
(encode directory (mhash 'tables new-tables) port)
(void))

@ -0,0 +1,58 @@
#lang debug racket
(require xenomorph
"tables.rkt"
"struct.rkt"
"zlib.rkt"
(for-syntax "tables.rkt"))
(provide (all-defined-out))
(define-syntax (define-table-getters stx)
(syntax-case stx ()
[(_)
(with-syntax ([(TABLE-TAG ...) (hash-keys table-codecs)])
(with-syntax ([(GETTER-ID ...) (map (λ (tag) (datum->syntax stx (string->symbol (format "get-~a-table" (syntax->datum tag)))))
(syntax->list #'(TABLE-TAG ...)))]
[(HAS-ID? ...) (map (λ (tag) (datum->syntax stx (string->symbol (format "has-~a-table?" (syntax->datum tag)))))
(syntax->list #'(TABLE-TAG ...)))])
#'(begin
(define (GETTER-ID this) (get-table this 'TABLE-TAG)) ...
(define (HAS-ID? this) (has-table? this 'TABLE-TAG)) ...)))]))
(define (has-table? this tag)
#;((or/c bytes? symbol?) . ->m . boolean?)
(define directory (force (ttf-font-directory this)))
(hash-has-key? (hash-ref directory 'tables) (match tag
[(? bytes?) (string->symbol (bytes->string/latin-1 tag))]
[_ tag])))
(define (get-table this table-tag)
(unless (has-table? this table-tag)
(raise-argument-error 'get-table "table that exists in font" table-tag))
(hash-ref! (ttf-font-decoded-tables this) table-tag (λ () (decode-table this table-tag))))
(define-table-getters)
(define (get-table-stream this tag)
(define directory (force (ttf-font-directory this)))
(define table (hash-ref (hash-ref directory 'tables) tag))
(and table
(pos (ttf-font-port this) (hash-ref table 'offset))
(if (and (woff-font? this) (< (hash-ref table 'compLength) (hash-ref table 'length)))
(open-input-bytes (inflate (peek-bytes (hash-ref table 'compLength) 0 (ttf-font-port this))))
(ttf-font-port this))))
(define (decode-table this table-tag)
(unless (hash-has-key? table-codecs table-tag)
(raise-argument-error 'decode-table "decodable table" table-tag))
(define last-pos (pos (ttf-font-port this)))
(define stream (get-table-stream this table-tag))
(define table-decoder (hash-ref table-codecs table-tag))
(define directory (force (ttf-font-directory this)))
(define table (hash-ref (hash-ref directory 'tables) table-tag))
(begin0
(decode table-decoder stream #:parent this (hash-ref table 'length))
(pos (ttf-font-port this) last-pos)))

@ -0,0 +1,5 @@
#lang debug racket/base
(require "cff/cff-font.rkt")
;; the CFFFont object acts as the decoder for the `CFF ` table.
(provide (rename-out [CFFFont CFF_]))

@ -0,0 +1,73 @@
#lang racket/base
(require xenomorph sugar/unstable/dict)
(provide OS/2)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/OS2.js
|#
(define OS/2 (let ()
(define type-1
(dictify 'typoAscender int16be
'typoDescender int16be
'typoLineGap int16be
'winAscent uint16be
'winDescent uint16be
'codePageRange (x:array #:type uint32be #:length 2)))
(define type-2
(dictify 'xHeight int16be
'capHeight int16be
'defaultChar uint16be
'breakChar uint16be
'maxContent uint16be))
(define type-5
(dictify 'usLowerOpticalPointSize uint16be
'usUpperOpticalPointSize uint16be))
(x:versioned-struct
uint16be
(dictify
'header (dictify 'xAvgCharWidth int16be ;; average weighted advance width of lower case letters and space
'usWeightClass uint16be ;; visual weight of stroke in glyphs
'usWidthClass uint16be ;; relative change from the normal aspect ratio (width to height ratio)
;; Indicates font embedding licensing rights
'fsType (x:bitfield #:type uint16be
#:flags '(#f noEmbedding viewOnly editable #f #f #f #f noSubsetting bitmapOnly))
'ySubscriptXSize int16be ;; recommended horizontal size in pixels for subscripts
'ySubscriptYSize int16be ;; recommended vertical size in pixels for subscripts
'ySubscriptXOffset int16be ;; recommended horizontal offset for subscripts
'ySubscriptYOffset int16be ;; recommended vertical offset form the baseline for subscripts
'ySuperscriptXSize int16be ;; recommended horizontal size in pixels for superscripts
'ySuperscriptYSize int16be ;; recommended vertical size in pixels for superscripts
'ySuperscriptXOffset int16be ;; recommended horizontal offset for superscripts
'ySuperscriptYOffset int16be ;; recommended vertical offset from the baseline for superscripts
'yStrikeoutSize int16be ;; width of the strikeout stroke
'yStrikeoutPosition int16be ;; position of the strikeout stroke relative to the baseline
'sFamilyClass int16be ;; classification of font-family design
'panose (x:array #:type uint8 #:length 10) ;; describe the visual characteristics of a given typeface
'ulCharRange (x:array #:type uint32be #:length 4)
'vendorID (x:symbol #:length 4) ;; four character identifier for the font vendor
;; bit field containing information about the font
'fsSelection (x:bitfield #:type uint16
#:flags '(italic underscore negative outlined strikeout bold regular useTypoMetrics wws oblique))
'usFirstCharIndex uint16be ;; The minimum Unicode index in this font
'usLastCharIndex uint16be) ;; The maximum Unicode index in this font
0 null
1 type-1
2 (append type-1 type-2)
3 (append type-1 type-2)
4 (append type-1 type-2)
5 (append type-1 type-2 type-5)))))
(module+ test
(require rackunit racket/serialize "../helper.rkt")
(define ip (open-input-file charter-path))
(define dir (deserialize (read (open-input-file charter-directory-path))))
(define offset (hash-ref (hash-ref (hash-ref dir 'tables) 'OS/2) 'offset))
(define len (hash-ref (hash-ref (hash-ref dir 'tables) 'OS/2) 'length))
(check-equal? offset 360)
(check-equal? len 96))

@ -0,0 +1,104 @@
#lang racket/base
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFCharsets.js
|#
(define ISOAdobeCharset
'(.notdef space exclam quotedbl numbersign dollar
percent ampersand quoteright parenleft parenright
asterisk plus comma hyphen period slash zero
one two three four five six seven eight
nine colon semicolon less equal greater question
at A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z
bracketleft backslash bracketright asciicircum underscore
quoteleft a b c d e f g h i j k l
m n o p q r s t u v w x y z
braceleft bar braceright asciitilde exclamdown cent
sterling fraction yen florin section currency
quotesingle quotedblleft guillemotleft guilsinglleft
guilsinglright fi fl endash dagger daggerdbl
periodcentered paragraph bullet quotesinglbase
quotedblbase quotedblright guillemotright ellipsis
perthousand questiondown grave acute circumflex tilde
macron breve dotaccent dieresis ring cedilla
hungarumlaut ogonek caron emdash AE ordfeminine
Lslash Oslash OE ordmasculine ae dotlessi lslash
oslash oe germandbls onesuperior logicalnot mu
trademark Eth onehalf plusminus Thorn onequarter
divide brokenbar degree thorn threequarters twosuperior
registered minus eth multiply threesuperior copyright
Aacute Acircumflex Adieresis Agrave Aring Atilde
Ccedilla Eacute Ecircumflex Edieresis Egrave Iacute
Icircumflex Idieresis Igrave Ntilde Oacute Ocircumflex
Odieresis Ograve Otilde Scaron Uacute Ucircumflex
Udieresis Ugrave Yacute Ydieresis Zcaron aacute
acircumflex adieresis agrave aring atilde ccedilla
eacute ecircumflex edieresis egrave iacute icircumflex
idieresis igrave ntilde oacute ocircumflex odieresis
ograve otilde scaron uacute ucircumflex udieresis
ugrave yacute ydieresis zcaron))
(define ExpertCharset
'(.notdef space exclamsmall Hungarumlautsmall dollaroldstyle
dollarsuperior ampersandsmall Acutesmall parenleftsuperior
parenrightsuperior twodotenleader onedotenleader comma
hyphen period fraction zerooldstyle oneoldstyle
twooldstyle threeoldstyle fouroldstyle fiveoldstyle
sixoldstyle sevenoldstyle eightoldstyle nineoldstyle
colon semicolon commasuperior threequartersemdash
periodsuperior questionsmall asuperior bsuperior
centsuperior dsuperior esuperior isuperior lsuperior
msuperior nsuperior osuperior rsuperior ssuperior
tsuperior ff fi fl ffi ffl parenleftinferior
parenrightinferior Circumflexsmall hyphensuperior Gravesmall
Asmall Bsmall Csmall Dsmall Esmall Fsmall Gsmall
Hsmall Ismall Jsmall Ksmall Lsmall Msmall Nsmall
Osmall Psmall Qsmall Rsmall Ssmall Tsmall Usmall
Vsmall Wsmall Xsmall Ysmall Zsmall colonmonetary
onefitted rupiah Tildesmall exclamdownsmall centoldstyle
Lslashsmall Scaronsmall Zcaronsmall Dieresissmall
Brevesmall Caronsmall Dotaccentsmall Macronsmall
figuredash hypheninferior Ogoneksmall Ringsmall
Cedillasmall onequarter onehalf threequarters
questiondownsmall oneeighth threeeighths fiveeighths
seveneighths onethird twothirds zerosuperior onesuperior
twosuperior threesuperior foursuperior fivesuperior
sixsuperior sevensuperior eightsuperior ninesuperior
zeroinferior oneinferior twoinferior threeinferior
fourinferior fiveinferior sixinferior seveninferior
eightinferior nineinferior centinferior dollarinferior
periodinferior commainferior Agravesmall Aacutesmall
Acircumflexsmall Atildesmall Adieresissmall Aringsmall
AEsmall Ccedillasmall Egravesmall Eacutesmall
Ecircumflexsmall Edieresissmall Igravesmall Iacutesmall
Icircumflexsmall Idieresissmall Ethsmall Ntildesmall
Ogravesmall Oacutesmall Ocircumflexsmall Otildesmall
Odieresissmall OEsmall Oslashsmall Ugravesmall Uacutesmall
Ucircumflexsmall Udieresissmall Yacutesmall Thornsmall
Ydieresissmall))
(define ExpertSubsetCharset
'(.notdef space dollaroldstyle dollarsuperior
parenleftsuperior parenrightsuperior twodotenleader
onedotenleader comma hyphen period fraction
zerooldstyle oneoldstyle twooldstyle threeoldstyle
fouroldstyle fiveoldstyle sixoldstyle sevenoldstyle
eightoldstyle nineoldstyle colon semicolon commasuperior
threequartersemdash periodsuperior asuperior bsuperior
centsuperior dsuperior esuperior isuperior lsuperior
msuperior nsuperior osuperior rsuperior ssuperior
tsuperior ff fi fl ffi ffl parenleftinferior
parenrightinferior hyphensuperior colonmonetary onefitted
rupiah centoldstyle figuredash hypheninferior onequarter
onehalf threequarters oneeighth threeeighths fiveeighths
seveneighths onethird twothirds zerosuperior onesuperior
twosuperior threesuperior foursuperior fivesuperior
sixsuperior sevensuperior eightsuperior ninesuperior
zeroinferior oneinferior twoinferior threeinferior
fourinferior fiveinferior sixinferior seveninferior
eightinferior nineinferior centinferior dollarinferior
periodinferior commainferior))

@ -0,0 +1,135 @@
#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* x0 x1 _) (bitwise-ior (arithmetic-shift x0 8) x1)]
[val val]))
(define CFFDict%
(class x:base%
(super-new)
(init-field [(@name name)] [(@ops ops)])
(field [(@fields fields)
(for/hash ([field (in-list @ops)])
(values (op->key field) field))])
(define (decode-operands type stream ret operands)
(match type
[(? list?) (for/list ([op (in-list operands)]
[subtype (in-list type)])
(decode-operands subtype stream ret (list op)))]
[(? xenomorphic?) (send type x:decode stream ret operands)]
[(or 'number 'offset 'sid) (car operands)]
['boolean (if (car operands) #t #f)]
[_ operands]))
(define (encode-operands type stream ctx operands)
(match type
[(? list?)
(for/list ([op (in-list operands)]
[subtype (in-list type)])
(car (encode-operands subtype stream ctx op)))]
[(? xenomorphic?) type (send type x:encode operands stream ctx)]
[_ (match operands
[(? number?) (list operands)]
[(? boolean?) (list (if operands 1 0))]
[(? list?) operands]
[_ (list operands)])]))
(define/override (post-decode val)
(dict->mutable-hash val))
(define/augment (x:decode stream parent)
(define end (+ (pos stream) (hash-ref parent 'length)))
(define ret (make-hasheq))
;; 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 ([operands null])
(when (< (pos stream) end)
(define b (read-byte stream))
(let bloop ([b b])
(cond
[(< b 28)
(let ([b (if (= b 12)
(bitwise-ior (arithmetic-shift b 8) (read-byte stream))
b)])
(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)]))]
[else
(loop (append operands (list (decode CFFOperand stream b))))]))))
ret)
(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)))
(+ (for*/sum ([k (in-list (sort (dict-keys @fields) <))]
[field (in-value (dict-ref @fields k))]
[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))
(define operand-size (for/sum ([op (in-list operands)])
(send CFFOperand x:size op)))
(define key (if (list? (first field)) (first field) (list (first field))))
(+ operand-size (length key)))
(if include-pointers (hash-ref ctx x:pointer-size-key) 0)))
(define/augment (x:encode dict stream parent)
(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))
(hash-set! ctx x:pointer-offset-key (+ (pos stream) (x:size dict ctx #false)))
(for ([field (in-list @ops)])
(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))]))
(let loop ([i 0])
(when (< i (length (hash-ref ctx x:pointers-key)))
(match (list-ref (hash-ref ctx x:pointers-key) i)
[(x:ptr type val parent) (send type x:encode val stream parent)])
(loop (add1 i)))))))
(define (CFFDict [name 'unknown] [ops null]) (make-object CFFDict% name ops))

@ -0,0 +1,52 @@
#lang racket/base
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFEncodings.js
|#
(define StandardEncoding '(|| || || || || || || || || || || || || || || || || || || || || || || || || || || ||
|| || || || space exclam quotedbl numbersign dollar percent ampersand quoteright
parenleft parenright asterisk plus comma hyphen period slash zero one two
three four five six seven eight nine colon semicolon less equal greater
question at A B C D E F G H I J K L M N O P Q R S
T U V W X Y Z bracketleft backslash bracketright asciicircum underscore
quoteleft a b c d e f g h i j k l m n o p q r s t
u v w x y z braceleft bar braceright asciitilde || || || || || || || ||
|| || || || || || || || || || || || || || || || || || || || || || || || || ||
exclamdown cent sterling fraction yen florin section currency quotesingle
quotedblleft guillemotleft guilsinglleft guilsinglright fi fl || endash dagger
daggerdbl periodcentered || paragraph bullet quotesinglbase quotedblbase quotedblright
guillemotright ellipsis perthousand || questiondown || grave acute circumflex tilde
macron breve dotaccent dieresis || ring cedilla || hungarumlaut ogonek caron
emdash || || || || || || || || || || || || || || || || AE || ordfeminine || || ||
|| Lslash Oslash OE ordmasculine || || || || || ae || || || dotlessi || ||
lslash oslash oe germandbls))
(define ExpertEncoding '(|| || || || || || || || || || || || || || || || || || || || || || || || || || || ||
|| || || || space exclamsmall Hungarumlautsmall || dollaroldstyle dollarsuperior
ampersandsmall Acutesmall parenleftsuperior parenrightsuperior twodotenleader onedotenleader
comma hyphen period fraction zerooldstyle oneoldstyle twooldstyle threeoldstyle
fouroldstyle fiveoldstyle sixoldstyle sevenoldstyle eightoldstyle nineoldstyle colon
semicolon commasuperior threequartersemdash periodsuperior questionsmall || asuperior
bsuperior centsuperior dsuperior esuperior || || isuperior || || lsuperior msuperior
nsuperior osuperior || || rsuperior ssuperior tsuperior || ff fi fl ffi ffl
parenleftinferior || parenrightinferior Circumflexsmall hyphensuperior Gravesmall Asmall
Bsmall Csmall Dsmall Esmall Fsmall Gsmall Hsmall Ismall Jsmall Ksmall Lsmall
Msmall Nsmall Osmall Psmall Qsmall Rsmall Ssmall Tsmall Usmall Vsmall Wsmall
Xsmall Ysmall Zsmall colonmonetary onefitted rupiah Tildesmall || || || || || || ||
|| || || || || || || || || || || || || || || || || || || || || || || || || || ||
exclamdownsmall centoldstyle Lslashsmall || || Scaronsmall Zcaronsmall Dieresissmall
Brevesmall Caronsmall || Dotaccentsmall || || Macronsmall || || figuredash hypheninferior
|| || Ogoneksmall Ringsmall Cedillasmall || || || onequarter onehalf threequarters
questiondownsmall oneeighth threeeighths fiveeighths seveneighths onethird twothirds ||
|| zerosuperior onesuperior twosuperior threesuperior foursuperior fivesuperior
sixsuperior sevensuperior eightsuperior ninesuperior zeroinferior oneinferior twoinferior
threeinferior fourinferior fiveinferior sixinferior seveninferior eightinferior
nineinferior centinferior dollarinferior periodinferior commainferior Agravesmall
Aacutesmall Acircumflexsmall Atildesmall Adieresissmall Aringsmall AEsmall Ccedillasmall
Egravesmall Eacutesmall Ecircumflexsmall Edieresissmall Igravesmall Iacutesmall
Icircumflexsmall Idieresissmall Ethsmall Ntildesmall Ogravesmall Oacutesmall
Ocircumflexsmall Otildesmall Odieresissmall OEsmall Oslashsmall Ugravesmall Uacutesmall
Ucircumflexsmall Udieresissmall Yacutesmall Thornsmall Ydieresissmall))

@ -0,0 +1,134 @@
#lang debug racket/base
(require racket/class racket/match racket/list xenomorph
"cff-top.rkt"
"cff-standard-strings.rkt"
fontland/struct)
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFFont.js
|#
;; the CFFFont object acts as the decoder for the `CFF ` table.
;; so it should return a hash.
(define CFFFont%
(class x:base%
(super-new)
(define/augride (x:decode stream parent . _)
(define cff-font (make-hasheq))
(hash-set! cff-font 'stream stream)
(for ([(k v) (in-hash (decode CFFTop stream))])
(hash-set! cff-font k v))
(when (and (hash-has-key? cff-font 'version) (< (hash-ref cff-font 'version) 2))
(match (hash-ref cff-font 'topDictIndex)
[(vector dict) (hash-set! cff-font 'topDict dict)]
[_ (error 'only-single-font-allowed-in-cff)]))
(hash-set! cff-font 'isCIDFont (hash-ref (hash-ref cff-font 'topDict) 'ROS))
cff-font)))
(define (CFFont-string this sid)
(cond
[(not sid) #false]
[(>= (hash-ref this 'version) 2) #false]
[(< sid (vector-length standard-strings)) (vector-ref standard-strings sid)]
[else (vector-ref (hash-ref this 'stringIndex) (- sid (vector-length standard-strings)))]))
(define (CFFFont-postscriptName this)
(and (< (hash-ref this 'version) 2) (vector-ref (hash-ref this 'nameIndex) 0)))
(define CFFFont (make-object CFFFont%))
(define (get-char-string cff-font glyph-id)
(define glyph-record (vector-ref (hash-ref (hash-ref cff-font 'topDict) 'CharStrings) glyph-id))
(pos (hash-ref cff-font 'stream) (index-item-offset glyph-record))
(read-bytes (index-item-length glyph-record) (hash-ref cff-font 'stream)))
(define (fd-for-glyph this gid)
(cond
[(not (hash-has-key? (hash-ref this 'topDict) 'FDSelect)) #false]
[else
(match (hash-ref* this 'topDict 'FDSelect 'version)
[0 (list-ref (hash-ref* this 'topDict 'FDSelect) gid)]
[(or 3 4)
(define ranges (hash-ref* this 'topDict 'FDSelect 'ranges))
(let loop ([low 0][high (sub1 (length ranges))])
(when (<= low high)
(define mid (arithmetic-shift (+ low high) -1))
(cond
[(< gid (hash-ref (list-ref ranges mid) 'first))
(loop low (sub1 mid))]
[(and (< mid high) (> gid (hash-ref (list-ref ranges (add1 mid)) 'first)))
(loop (add1 mid) high)]
[else (hash-ref (list-ref ranges mid) 'fd)])))]
[default (error 'unknown-select-version)])]))
(define (private-dict-for-glyph this gid)
(cond
[(and (hash-has-key? this 'topDict)
(hash-has-key? (hash-ref this 'topDict) 'FDSelect)
(hash-ref* this 'topDict 'FDSelect))
(define fd (fd-for-glyph this gid))
(if (list-ref (hash-ref* this 'topDict 'FDArray) fd)
(hash-ref (list-ref (hash-ref* 'topDict 'FDArray) fd) 'Private)
#false)]
[(< (hash-ref this 'version) 2) (hash-ref* this 'topDict 'Private)]
[else (hash-ref (list-ref (hash-ref* this 'topDict 'FDArray) 0) 'Private)]))
(module+ test
(require rackunit racket/serialize racket/stream fontland/helper)
(define dir (deserialize (read (open-input-file fira-otf-directory-path))))
(define cff (hash-ref (hash-ref dir 'tables) 'CFF_))
(check-equal? (hash-ref cff 'length) 164604)
(define ip (open-input-file fira-otf-path))
(define cff-offset (hash-ref cff 'offset))
(check-equal? cff-offset 33472)
(file-position ip cff-offset)
(define cff-font (decode CFFFont ip))
(check-equal? (file-position (hash-ref cff-font 'stream)) 74651)
(check-equal? (hash-ref cff-font 'version) 1)
(check-equal? (hash-ref cff-font 'hdrSize) 4)
(check-equal? (hash-ref cff-font 'offSize) 3)
(check-equal? (hash-ref cff-font 'nameIndex) '#("FiraSans-Book"))
(check-equal? (vector-length (hash-ref cff-font 'globalSubrIndex)) 820)
(check-equal?
(for/list ([h (in-vector (hash-ref cff-font 'globalSubrIndex))])
(index-item-offset h))
'(60105 60130 60218 60264 60303 60330 60361 60366 60387 60427 60433 60447 60454 60469 60500 60506 60512 60516 60545 60566 60581 60624 60637 60667 60679 60705 60715 60755 60776 60781 60839 60891 60897 60907 60914 60920 60938 60950 60976 60992 61005 61011 61032 61051 61067 61097 61111 61172 61272 61284 61359 61430 61489 61522 61526 61531 61535 61543 61565 61570 61575 61579 61601 61615 61629 61649 61654 61664 61842 61849 61858 61865 61895 61913 61920 61964 61977 61996 62074 62094 62102 62128 62132 62149 62160 62170 62197 62216 62225 62230 62237 62247 62256 62285 62332 62339 62347 62350 62375 62435 62479 62511 62539 62561 62585 62605 62621 62632 62711 62717 62733 62743 62783 62809 62818 62868 62905 62955 62965 62971 63034 63050 63059 63191 63237 63358 63394 63460 63465 63592 63716 63740 63866 63924 63947 64051 64075 64099 64120 64184 64245 64260 64374 64493 64515 64543 64585 64592 64597 64611 64622 64735 64738 64789 64797 64882 64920 65027 65054 65057 65069 65077 65113 65125 65222 65254 65275 65377 65480 65516 65524 65530 65550 65565 65569 65576 65673 65691 65760 65836 65854 65866 65873 65881 65895 65924 65929 65949 65970 66060 66093 66113 66132 66146 66151 66160 66165 66174 66185 66192 66210 66231 66255 66280 66288 66296 66301 66386 66395 66400 66446 66455 66537 66545 66550 66555 66636 66712 66722 66729 66748 66774 66788 66797 66810 66818 66841 66847 66853 66872 66877 66882 66887 66962 66988 66997 67008 67021 67027 67034 67040 67047 67110 67180 67218 67256 67325 67355 67369 67376 67390 67399 67403 67471 67478 67499 67520 67524 67550 67565 67579 67584 67651 67671 67679 67684 67749 67759 67772 67783 67790 67817 67883 67944 67967 67986 68049 68056 68090 68113 68132 68139 68149 68154 68159 68222 68226 68259 68262 68323 68326 68335 68372 68413 68420 68427 68435 68441 68446 68451 68462 68477 68489 68530 68535 68548 68553 68560 68567 68622 68638 68694 68748 68759 68764 68816 68862 68880 68885 68900 68907 68959 68988 69002 69011 69016 69028 69037 69089 69099 69115 69131 69143 69152 69160 69168 69174 69180 69202 69213 69218 69268 69318 69325 69374 69383 69402 69415 69422 69427 69434 69446 69488 69514 69529 69535 69582 69587 69603 69647 69667 69678 69684 69690 69700 69705 69710 69752 69795 69816 69860 69888 69898 69912 69921 69932 69936 69943 69948 69991 70002 70028 70041 70051 70057 70099 70130 70151 70166 70207 70219 70257 70279 70290 70300 70309 70316 70325 70333 70341 70346 70352 70357 70364 70401 70438 70475 70480 70487 70491 70497 70502 70532 70545 70552 70557 70562 70599 70616 70647 70651 70658 70665 70670 70706 70712 70737 70754 70766 70778 70786 70798 70804 70812 70817 70823 70858 70893 70904 70908 70913 70926 70933 70947 70954 70962 70968 70976 70981 70987 70996 71002 71007 71012 71018 71041 71074 71079 71111 71143 71175 71192 71196 71207 71215 71220 71226 71234 71247 71254 71261 71291 71308 71314 71322 71339 71345 71350 71354 71379 71395 71404 71413 71417 71422 71436 71458 71463 71479 71491 71501 71509 71521 71528 71535 71541 71547 71555 71561 71568 71575 71582 71588 71594 71599 71626 71640 71666 71692 71696 71704 71722 71735 71750 71759 71766 71781 71792 71797 71802 71808 71813 71818 71840 71865 71890 71894 71905 71910 71916 71927 71935 71942 71949 71956 71961 71966 71977 72001 72014 72036 72044 72056 72070 72076 72082 72087 72093 72100 72112 72135 72151 72155 72163 72172 72177 72182 72195 72217 72239 72261 72277 72297 72304 72324 72337 72350 72355 72365 72370 72377 72387 72394 72398 72405 72412 72417 72423 72429 72446 72460 72465 72485 72495 72514 72523 72530 72535 72546 72566 72586 72604 72624 72642 72651 72660 72666 72678 72684 72689 72698 72707 72714 72722 72730 72735 72740 72746 72752 72758 72774 72793 72799 72812 72825 72834 72839 72858 72862 72868 72877 72886 72895 72904 72913 72922 72931 72940 72946 72953 72960 72967 72973 72978 72985 73000 73007 73025 73043 73048 73066 73073 73089 73100 73111 73118 73124 73135 73140 73146 73152 73158 73163 73168 73173 73186 73200 73217 73234 73249 73264 73279 73286 73302 73318 73332 73348 73358 73374 73384 73390 73395 73403 73413 73422 73428 73438 73445 73452 73459 73465 73473 73479 73486 73491 73497 73505 73513 73521 73528 73533 73538 73545 73552 73559 73566 73572 73587 73592 73604 73619 73634 73649 73664 73679 73694 73709 73722 73735 73743 73748 73753 73758 73771 73784 73798 73804 73814 73827 73839 73843 73851 73860 73865 73872 73881 73890 73899 73908 73914 73920 73926 73932 73938 73943 73948 73953 73963 73976 73989 74002 74015 74028 74037 74050 74063 74076 74087 74098 74104 74110 74117 74123 74130 74137 74144 74149 74156 74161 74166 74171 74176 74188 74200 74212 74224 74236 74248 74260 74266 74278 74282 74294 74306 74313 74321 74328 74336 74342 74350 74357 74364 74370 74377 74385 74390 74396 74402 74408 74414 74420 74425 74430 74435 74440 74445 74450 74455 74466 74477 74488 74498 74509 74520 74531 74542 74553 74564 74575 74584 74593 74602 74611 74616 74621 74626 74631 74636 74641 74646))
(check-equal? (vector-length (hash-ref cff-font 'stringIndex)) 2404)
#;(check-equal? (hash-ref (hash-ref cff-font 'topDict) 'version) 2401)
(check-equal?
(vector-ref (hash-ref cff-font 'stringIndex) 2401)
"004.106")
(check-equal?
(vector-ref (hash-ref cff-font 'stringIndex) 2402)
"Digitized data copyright \\(c\\) 2012-2015, The Mozilla Foundation and Telefonica S.A.")
(check-equal?
(vector-ref (hash-ref cff-font 'stringIndex) 2403)
"Fira Sans Book")
(define top-dict (hash-ref cff-font 'topDict))
(check-equal? (hash-ref top-dict 'FontBBox) '(-167 -350 1360 1093))
(check-equal? (hash-ref top-dict 'version) 2792)
(check-equal? (hash-ref top-dict 'Notice) 2793)
(check-equal? (hash-ref top-dict 'FullName) 2794)
(check-equal? (hash-ref top-dict 'Weight) 388)
(define private (hash-ref top-dict 'Private))
(check-equal? (hash-ref private 'StdHW) 68)
(check-equal? (hash-ref private 'StdVW) 84)
(check-equal? (hash-ref private 'defaultWidthX) 0)
(check-equal? (hash-ref private 'nominalWidthX) 553)
(check-equal? (hash-ref private 'BlueScale) 0.037)
(check-equal? (hash-ref private 'BlueShift) 7)
(check-equal? (hash-ref private 'ExpansionFactor) 0.06)
(check-equal?
(for/list ([h (in-list (take (vector->list (hash-ref top-dict 'CharStrings)) 100))])
(index-item-offset h))
'(83610 83750 83753 83755 83776 83778 83810 83858 83890 83951 84023 84046 84068 84096 84132 84169 84233 84270 84292 84322 84380 84411 84439 84478 84498 84547 84575 84679 84711 84751 84784 84823 84919 84956 84964 84978 85011 85013 85101 85188 85300 85302 85396 85398 85407 85422 85436 85451 85547 85561 85587 85647 85784 85790 85824 85864 85933 85935 85960 85970 85972 86003 86027 86091 86106 86161 86176 86228 86238 86253 86273 86288 86347 86363 86385 86401 86423 86463 86496 86511 86541 86568 86578 86594 86627 86651 86680 86731 86733 86766 86769 86861 86887 86900 86919 86986 87017 87061 87098 87108))
)

@ -0,0 +1,117 @@
#lang debug racket
(require racket/class racket/match xenomorph sugar/unstable/dict fontland/struct)
(provide CFFIndex)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFIndex.js
|#
(define CFFIndex%
(class x:base%
(super-new)
(init-field [(@type type) #f])
(define (getCFFVersion ctx)
(let loop ([ctx ctx])
(cond
[(and ctx (hash? ctx) (not (hash-ref ctx 'hdrSize #f)))
(loop (hash-ref ctx 'x:parent))]
[(and ctx (hash-ref ctx 'x:version #f))]
[else -1])))
(define/augride (x:decode stream parent)
(match (decode (if (>= (getCFFVersion parent) 2) uint32be uint16be) stream)
[0 (vector)]
[count (define offSize (decode uint8 stream))
(define offsetType (match offSize
[1 uint8]
[2 uint16be]
[3 uint24be]
[4 uint32be]
[_ (error (format "bad-offset-size-in-CFFIndex ~a" offSize))]))
(define startPos (+ (pos stream) (* (add1 count) offSize) -1))
(for/fold ([vals null]
[start (send offsetType x:decode stream)]
#:result (begin0 (list->vector (reverse vals)) (pos stream (+ startPos start))))
([i (in-range count)])
(define end (send offsetType x:decode stream))
(define val
(cond
[@type
(define apos (pos stream))
(pos stream (+ startPos start))
(hash-set! parent 'length (- end start))
(begin0
(send @type x:decode stream parent)
(pos stream apos))]
[else
(index-item (+ startPos start) (- end start))]))
(values (cons val vals) end))]))
(define/augride (x:size arr-arg parent)
(define arr (match arr-arg
[(? list? xs) (list->vector xs)]
[vec vec]))
(+ 2
(cond
[(zero? (vector-length arr)) 0]
[else (define type (or @type (x:buffer)))
;; find maximum offset to determinine offset type
(define offset
(add1 (for/sum ([item (in-vector arr)])
(send type x:size item parent))))
(define offset-type
(cond
[(<= offset #xff) uint8]
[(<= offset #xffff) uint16be]
[(<= offset #xffffff) uint24be]
[(<= offset #xffffffff) uint32be]
[else (error 'CFFIndex-size (format "bad offset: ~a" offset))]))
(+ (* (send offset-type x:size) (add1 (vector-length arr))) offset)])))
(define/augride (x:encode arr-arg stream parent)
(define arr (match arr-arg
[(? list? xs) (list->vector xs)]
[vec vec]))
(send uint16be x:encode (vector-length arr) stream)
(cond
[(zero? (vector-length arr))]
[else
(define type (or @type (x:buffer)))
;; find maximum offset to detminine offset type
(define-values (sizes offset)
(for/fold ([sizes null]
[offset 1]
#:result (values (reverse sizes) offset))
([item (in-vector arr)])
(define s (send type x:size item parent))
(values (cons s sizes) (+ offset s))))
(define offsetType
(cond
[(<= offset #xff) uint8]
[(<= offset #xffff) uint16be]
[(<= offset #xffffff) uint24be]
[(<= offset #xffffffff) uint32be]
[else (error 'cff-index-encode-bad-offset!)]))
;; write offset size
(send uint8 x:encode (send offsetType x:size) stream)
;; write elements
(for/fold ([offset 1])
([size (in-list (cons 0 sizes))])
(define next-offset (+ offset size))
(send offsetType x:encode next-offset stream)
next-offset)
(for ([item (in-vector arr)])
(send type x:encode item stream parent))]))))
(define (CFFIndex [type #f])
(new CFFIndex% [type type]))

@ -0,0 +1,115 @@
#lang debug racket/base
(require racket/class xenomorph "cff-struct.rkt")
(provide CFFOperand)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFOperand.js
|#
(define FLOAT_EOF #xf)
(define FLOAT_LOOKUP (vector "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "." "E" "E-" "" "-"))
(define FLOAT_ENCODE_LOOKUP
(hash "." 10
"E" 11
"E-" 12
"-" 14))
(define CFFOperand%
(class x:base%
(super-new)
(define/augment (x:decode stream _ value)
(cond
[(<= 32 value 246) (- value 139)]
[(<= 247 value 250) (+ (* (- value 247) 256) (read-byte stream) 108)]
[(<= 251 value 254) (- (* (- 251 value) 256) (read-byte stream) 108)]
[(= value 28) (decode int16be stream)]
[(= value 29) (decode int32be stream)]
[(= value 30)
(for/fold ([strs null]
[break? #false]
#:result (* (string->number (apply string-append (reverse strs))) 1.0))
([i (in-naturals)]
#:break break?)
(define b (read-byte stream))
(define n1 (arithmetic-shift b -4))
(cond
[(= n1 FLOAT_EOF) (values strs 'break-now)]
[else
(let ([strs (cons (vector-ref FLOAT_LOOKUP n1) strs)])
(define n2 (bitwise-and b 15))
(cond
[(= n2 FLOAT_EOF) (values strs 'break-now)]
[else
(let ([strs (cons (vector-ref FLOAT_LOOKUP n2) strs)])
(values strs #false))]))]))]))
(define/augment (x:size value-arg _)
;; if the value needs to be forced to the largest size (32 bit)
;; e.g. for unknown pointers, set to 32768
(define value (cond
[(Ptr? value-arg) (if (Ptr-forceLarge value-arg) 32768 (Ptr-val value-arg))]
[else value-arg]))
(cond
[(not (integer? value)) ; floating point
(define str (number->string value))
(add1 (ceiling (/ (add1 (string-length str)) 2)))]
[(<= -107 value 107) 1]
[(<= -1131 value 1131) 2]
[(<= -32768 value 32767) 3]
[else 5]))
(define (string->inexact str)
(string->number str 10 'number-or-false 'decimal-as-inexact))
(define/augment (x:encode value-arg stream . _)
;; if the value needs to be forced to the largest size (32 bit)
;; e.g. for unknown pointers, save the old value and set to 32768
(define value (if (Ptr? value-arg) (Ptr-val value-arg) value-arg))
(define val (if value (string->inexact (format "~a" value)) 0))
(cond
[(and (Ptr? value-arg) (Ptr-forceLarge value-arg))
(encode uint8 29 stream)
(encode int32be val stream)]
[(not (integer? val)) ;; floating point
(encode uint8 30 stream)
(define str (list->vector (regexp-match* #rx"." (number->string val))))
(define n2
(for/last ([i (in-range 0 (vector-length str) 2)])
(define c1 (vector-ref str i))
(define n1 (hash-ref FLOAT_ENCODE_LOOKUP c1 (string->number c1)))
(define n2
(cond
[(= i (sub1 (vector-length str))) FLOAT_EOF]
[else
(define c2 (vector-ref str (add1 i)))
(hash-ref FLOAT_ENCODE_LOOKUP c2 (string->number c2))]))
(encode uint8 (bitwise-ior (arithmetic-shift n1 4) (bitwise-and n2 15)) stream)
n2))
(unless (= n2 FLOAT_EOF)
(encode uint8 (arithmetic-shift FLOAT_EOF 4) stream))]
[(<= -107 value 107)
(encode uint8 (+ val 139) stream)]
[(<= 108 value 1131)
(let ([val (- val 108)])
(encode uint8 (+ (arithmetic-shift val -8) 247) stream)
(encode uint8 (bitwise-and val #xff) stream))]
[(<= -1131 value -108)
(let ([val (- (+ val 108))])
(encode uint8 (+ (arithmetic-shift val -8) 251) stream)
(encode uint8 (bitwise-and val #xff) stream))]
[(<= -32768 value 32767)
(encode uint8 28 stream)
(encode uint16be val stream)]
[else
(encode uint8 29 stream)
(encode uint32be val stream)]))))
(define CFFOperand (make-object CFFOperand%))

@ -0,0 +1,50 @@
#lang debug racket/base
(require racket/class racket/list xenomorph/pointer xenomorph/base "cff-struct.rkt")
(provide CFFPointer CFFPointer%)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFPointer.js
|#
(define (CFFPointer type
#:relative-to [relative-to 'global]
#:lazy [lazy #false])
(x:pointer #:dest-type type
#:base-class CFFPointer%
#:relative-to relative-to
#:lazy lazy))
(define CFFPointer%
(class x:pointer%
(super-new)
(inherit-field ptr-type)
(define/override (x:decode stream parent operands)
(set! ptr-type (make-object
(class x:base%
(super-new)
(define/augment (x:decode . args) (first operands)))))
(super x:decode stream parent))
(define/override (x:encode value stream ctx)
(cond
[(not stream)
;; compute the size (so ctx.pointerSize is correct)
(set! ptr-type (make-object
(class x:base%
(super-new)
(define/augment (x:size . args) 0))))
(send this x:size value ctx)
(list (Ptr 0))]
[else
(define ptr #false)
(set! ptr-type (make-object
(class x:base%
(super-new)
(define/augment (x:encode val stream . _) (set! ptr val)))))
(super x:encode value stream ctx)
(list (Ptr ptr))]))))

@ -0,0 +1,37 @@
#lang debug racket/base
(require racket/class racket/match xenomorph sugar/unstable/dict
"cff-dict.rkt"
"cff-index.rkt"
"cff-pointer.rkt")
(provide CFFPrivateDict)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFPrivateDict.js
|#
(define CFFPrivateDict
(CFFDict
'CFFPrivateDict
;; key name type default
`((6 BlueValues delta #false)
(7 OtherBlues delta #false)
(8 FamilyBlues delta #false)
(9 FamilyOtherBlues delta #false)
((12 9) BlueScale number 0.039625)
((12 10) BlueShift number 7)
((12 11) BlueFuzz number 1)
(10 StdHW number #false)
(11 StdVW number #false)
((12 12) StemSnapH delta #false)
((12 13) StemSnapV delta #false)
((12 14) ForceBold boolean #false)
((12 17) LanguageGroup number 0)
((12 18) ExpansionFactor number 0.06)
((12 19) initialRandomSeed number 0)
(20 defaultWidthX number 0)
(21 nominalWidthX number 0)
(22 vsindex number 0)
#;(23 blend boolean #false)
(19 Subrs ,(CFFPointer (CFFIndex) #:relative-to 'local) ,(vector)))))

@ -0,0 +1,76 @@
#lang racket/base
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFStandardStrings.js
|#
(define standard-strings
'#(".notdef" "space" "exclam" "quotedbl" "numbersign" "dollar"
"percent" "ampersand" "quoteright" "parenleft" "parenright"
"asterisk" "plus" "comma" "hyphen" "period" "slash" "zero" "one"
"two" "three" "four" "five" "six" "seven" "eight" "nine" "colon"
"semicolon" "less" "equal" "greater" "question" "at" "A" "B" "C"
"D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R"
"S" "T" "U" "V" "W" "X" "Y" "Z" "bracketleft" "backslash"
"bracketright" "asciicircum" "underscore" "quoteleft" "a" "b" "c"
"d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r"
"s" "t" "u" "v" "w" "x" "y" "z" "braceleft" "bar" "braceright"
"asciitilde" "exclamdown" "cent" "sterling" "fraction" "yen"
"florin" "section" "currency" "quotesingle" "quotedblleft"
"guillemotleft" "guilsinglleft" "guilsinglright" "fi" "fl" "endash"
"dagger" "daggerdbl" "periodcentered" "paragraph" "bullet"
"quotesinglbase" "quotedblbase" "quotedblright" "guillemotright"
"ellipsis" "perthousand" "questiondown" "grave" "acute" "circumflex"
"tilde" "macron" "breve" "dotaccent" "dieresis" "ring" "cedilla"
"hungarumlaut" "ogonek" "caron" "emdash" "AE" "ordfeminine" "Lslash"
"Oslash" "OE" "ordmasculine" "ae" "dotlessi" "lslash" "oslash" "oe"
"germandbls" "onesuperior" "logicalnot" "mu" "trademark" "Eth"
"onehalf" "plusminus" "Thorn" "onequarter" "divide" "brokenbar"
"degree" "thorn" "threequarters" "twosuperior" "registered" "minus"
"eth" "multiply" "threesuperior" "copyright" "Aacute" "Acircumflex"
"Adieresis" "Agrave" "Aring" "Atilde" "Ccedilla" "Eacute"
"Ecircumflex" "Edieresis" "Egrave" "Iacute" "Icircumflex" "Idieresis"
"Igrave" "Ntilde" "Oacute" "Ocircumflex" "Odieresis" "Ograve"
"Otilde" "Scaron" "Uacute" "Ucircumflex" "Udieresis" "Ugrave"
"Yacute" "Ydieresis" "Zcaron" "aacute" "acircumflex" "adieresis"
"agrave" "aring" "atilde" "ccedilla" "eacute" "ecircumflex"
"edieresis" "egrave" "iacute" "icircumflex" "idieresis" "igrave"
"ntilde" "oacute" "ocircumflex" "odieresis" "ograve" "otilde"
"scaron" "uacute" "ucircumflex" "udieresis" "ugrave" "yacute"
"ydieresis" "zcaron" "exclamsmall" "Hungarumlautsmall"
"dollaroldstyle" "dollarsuperior" "ampersandsmall" "Acutesmall"
"parenleftsuperior" "parenrightsuperior" "twodotenleader"
"onedotenleader" "zerooldstyle" "oneoldstyle" "twooldstyle"
"threeoldstyle" "fouroldstyle" "fiveoldstyle" "sixoldstyle"
"sevenoldstyle" "eightoldstyle" "nineoldstyle" "commasuperior"
"threequartersemdash" "periodsuperior" "questionsmall" "asuperior"
"bsuperior" "centsuperior" "dsuperior" "esuperior" "isuperior"
"lsuperior" "msuperior" "nsuperior" "osuperior" "rsuperior"
"ssuperior" "tsuperior" "ff" "ffi" "ffl" "parenleftinferior"
"parenrightinferior" "Circumflexsmall" "hyphensuperior" "Gravesmall"
"Asmall" "Bsmall" "Csmall" "Dsmall" "Esmall" "Fsmall" "Gsmall"
"Hsmall" "Ismall" "Jsmall" "Ksmall" "Lsmall" "Msmall" "Nsmall"
"Osmall" "Psmall" "Qsmall" "Rsmall" "Ssmall" "Tsmall" "Usmall"
"Vsmall" "Wsmall" "Xsmall" "Ysmall" "Zsmall" "colonmonetary"
"onefitted" "rupiah" "Tildesmall" "exclamdownsmall" "centoldstyle"
"Lslashsmall" "Scaronsmall" "Zcaronsmall" "Dieresissmall" "Brevesmall"
"Caronsmall" "Dotaccentsmall" "Macronsmall" "figuredash"
"hypheninferior" "Ogoneksmall" "Ringsmall" "Cedillasmall"
"questiondownsmall" "oneeighth" "threeeighths" "fiveeighths"
"seveneighths" "onethird" "twothirds" "zerosuperior" "foursuperior"
"fivesuperior" "sixsuperior" "sevensuperior" "eightsuperior"
"ninesuperior" "zeroinferior" "oneinferior" "twoinferior"
"threeinferior" "fourinferior" "fiveinferior" "sixinferior"
"seveninferior" "eightinferior" "nineinferior" "centinferior"
"dollarinferior" "periodinferior" "commainferior" "Agravesmall"
"Aacutesmall" "Acircumflexsmall" "Atildesmall" "Adieresissmall"
"Aringsmall" "AEsmall" "Ccedillasmall" "Egravesmall" "Eacutesmall"
"Ecircumflexsmall" "Edieresissmall" "Igravesmall" "Iacutesmall"
"Icircumflexsmall" "Idieresissmall" "Ethsmall" "Ntildesmall"
"Ogravesmall" "Oacutesmall" "Ocircumflexsmall" "Otildesmall"
"Odieresissmall" "OEsmall" "Oslashsmall" "Ugravesmall" "Uacutesmall"
"Ucircumflexsmall" "Udieresissmall" "Yacutesmall" "Thornsmall"
"Ydieresissmall" "001.000" "001.001" "001.002" "001.003" "Black"
"Bold" "Book" "Light" "Medium" "Regular" "Roman" "Semibold"))

@ -0,0 +1,3 @@
#lang racket/base
(provide (all-defined-out))
(struct Ptr (val [forceLarge #:auto]) #:transparent #:mutable #:auto-value #true)

@ -0,0 +1,222 @@
#lang debug racket/base
(require xenomorph
racket/list
racket/vector
racket/match
sugar/unstable/dict
racket/class
racket/dict
"cff-index.rkt"
"cff-dict.rkt"
"cff-charsets.rkt"
"cff-pointer.rkt"
"cff-encodings.rkt"
"cff-private-dict.rkt")
(provide CFFTop)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/cff/CFFTop.js
|#
(define PredefinedOp%
(class x:base%
(super-new)
(init-field [(@predefinedOps predefinedOps)]
[(@type type) #f])
(field [op-vec (list->vector @predefinedOps)])
(define/override (pre-encode val)
;; because fontkit depends on overloading 'version key, and we don't
(let ([val (make-hasheq val)])
(hash-set! val 'x:version (hash-ref val 'version))
val))
(define/augment (x:decode stream parent operands)
(define idx (car operands))
(if (< idx (vector-length op-vec))
(vector-ref op-vec idx)
(decode @type stream #:parent parent operands)))
(define/augment (x:size value ctx)
(error 'predefined-op-size-not-finished))
(define/augment (x:encode value stream ctx)
(or (vector-member value op-vec)
(send @type x:encode value stream ctx)))))
(define (PredefinedOp predefinedOps type) (make-object PredefinedOp% predefinedOps type))
(define CFFEncodingVersion
(x:int #:size 1
#:signed #false
#:post-decode (λ (res) (bitwise-and res #x7f))))
(define Range1
(x:struct
'first uint16be
'nLeft uint8))
(define Range2
(x:struct
'first uint16be
'nLeft uint16be))
(define CFFCustomEncoding
(x:versioned-struct
CFFEncodingVersion
(dictify
0 (dictify 'nCodes uint8
'codes (x:list #:type uint8 #:length (λ (p) (hash-ref p 'nCodes))))
1 (dictify 'nRanges uint8
'ranges (x:list #:type Range1 #:length (λ (p) (hash-ref p 'nRanges)))))))
(define CFFEncoding (PredefinedOp (list StandardEncoding ExpertEncoding)
(CFFPointer CFFCustomEncoding #:lazy #true)))
;; Decodes an array of ranges until the total
;; length is equal to the provided length.
(define RangeArray%
(class x:list%
(super-new)
(inherit-field [@len len] [@type type])
(define/override (x:decode stream parent)
(define length (resolve-length @len stream parent))
(for/fold ([res null]
[count 0]
#:result (reverse res))
([i (in-naturals)]
#:break (not (< count length)))
(define range (decode @type stream parent))
(hash-set! range 'offset count)
(values (cons range res) (+ count (hash-ref range 'nLeft) 1))))))
(define (RangeArray . args) (apply x:list #:base-class RangeArray% args))
(define (base-tproc t) (length (hash-ref (hash-ref t 'parent) 'CharStrings)))
(define CFFCustomCharset
(x:versioned-struct
uint8
(let ([tproc (λ (t) (sub1 (base-tproc t)))])
(dictify
0 (dictify 'glyphs (x:list uint16be tproc))
1 (dictify 'ranges (RangeArray Range1 tproc))
2 (dictify 'ranges (RangeArray Range2 tproc))))))
(define CFFCharset (PredefinedOp
(list ISOAdobeCharset ExpertCharset ExpertSubsetCharset)
(CFFPointer CFFCustomCharset #:lazy #true)))
(define FDRange3
(x:struct 'first uint16be
'fd uint8))
(define FDRange4
(x:struct 'first uint32be
'fd uint16be))
(define FDSelect
(x:versioned-struct
uint8
#:version-key 'version
(dictify
0 (dictify 'fds (x:list uint8 base-tproc))
3 (dictify 'nRanges uint16be
'ranges (x:list #:type FDRange3 #:length (λ (p) (hash-ref p 'nRanges)))
'sentinel uint16be)
4 (dictify 'nRanges uint32be
'ranges (x:list #:type FDRange4 #:length (λ (p) (hash-ref p 'nRanges)))
'sentinel uint32be))))
(define ptr (CFFPointer CFFPrivateDict))
(define CFFPrivateOp%
(class x:base%
(super-new)
(define/augment (x:decode stream parent operands)
(match operands
[(list op1 op2)
(hash-set! parent 'length op1)
(send ptr x:decode stream parent (list op2))]))
(define/augment (x:size dict ctx)
(list (send CFFPrivateDict x:size dict ctx #false)
(car (send ptr x:size dict ctx))))
(define/augment (x:encode dict stream ctx)
(list (send CFFPrivateDict x:size dict ctx #false)
(car (send ptr x:encode dict stream ctx))))))
(define (CFFPrivateOp)
(make-object CFFPrivateOp%))
(define FontDict
(CFFDict
'FontDict
;; key name type(s) default
`((18 Private ,(CFFPrivateOp) #false)
((12 38) FontName sid #false))))
(define CFFTopDict
(CFFDict
'CFFTopDict
;; key name type(s) default
`(((12 30) ROS (sid sid number) #false)
(0 version sid #false)
(1 Notice sid #false)
((12 0) Copyright sid #false)
(2 FullName sid #false)
(3 FamilyName sid #false)
(4 Weight sid #false)
((12 1) isFixedPitch boolean #false)
((12 2) ItalicAngle number 0)
((12 3) UnderlinePosition number -100)
((12 4) UnderlineThickness number 50)
((12 5) PaintType number 0)
((12 6) CharstringType number 2)
((12 7) FontMatrix array (0.001 0 0 0.001 0 0))
(13 UniqueID number #false)
(5 FontBBox array (0 0 0 0))
((12 8) StrokeWidth number 0)
(14 XUID array #false)
(15 charset ,CFFCharset ,ISOAdobeCharset)
(16 Encoding ,CFFEncoding ,StandardEncoding)
(17 CharStrings ,(CFFPointer (CFFIndex)) #false)
(18 Private ,(CFFPrivateOp) #false)
((12 20) SyntheticBase number #false)
((12 21) PostScript sid #false)
((12 22) BaseFontName sid #false)
((12 23) BaseFontBlend delta #false)
;; CID font specific
((12 31) CIDFontVersion number 0)
((12 32) CIDFontRevision number 0)
((12 33) CIDFontType number 0)
((12 34) CIDCount number 8720)
((12 35) UIDBase number #false)
((12 37) FDSelect ,(CFFPointer FDSelect) #false)
((12 36) FDArray ,(CFFPointer (CFFIndex FontDict)) #false)
((12 38) FontName sid #false))))
(define CFFTop
(x:versioned-struct
#:version-key 'version
fixed16be
(dictify
1 (dictify 'hdrSize uint8
'offSize uint8
'nameIndex (CFFIndex (x:string #:length (λ (p) (hash-ref p 'length))))
'topDictIndex (CFFIndex CFFTopDict)
'stringIndex (CFFIndex (x:string #:length (λ (p) (hash-ref p 'length))))
'globalSubrIndex (CFFIndex))
#|
2 (dictify 'hdrSize uint8
'length uint16be
'topDict CFF2TopDict
'globalSubrIndex (CFFIndex))
|#
)))

@ -0,0 +1,26 @@
#lang racket/base
(require xenomorph)
(provide cvt_)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/cvt.js
|#
(define cvt_ (x:struct 'controlValues (x:array #:type int16be)))
(module+ test
(require rackunit racket/serialize "../helper.rkt")
(define ip (open-input-file charter-path))
(define dir (deserialize (read (open-input-file charter-directory-path))))
(define offset (hash-ref (hash-ref (hash-ref dir 'tables) 'cvt_) 'offset))
(define len (hash-ref (hash-ref (hash-ref dir 'tables) 'cvt_) 'length))
(check-equal? offset 4592)
(check-equal? len 26)
(file-position ip 0)
(define table-bytes #"\0\24\0+\0S\0\0\0\20\377&\0\0\1\341\0\v\2\237\0\22\2\340\0\b")
(check-equal? table-bytes (peek-bytes len offset ip))
(define ds (open-input-bytes (peek-bytes len offset ip)))
(define cvt-array '(20 43 83 0 16 -218 0 481 11 671 18 736 8))
(check-equal? (hash-ref (decode cvt_ ds) 'controlValues) cvt-array)
(check-equal? (encode cvt_ (make-hash (list (cons 'controlValues cvt-array))) #f) table-bytes))

@ -0,0 +1,25 @@
#lang racket/base
(require xenomorph)
(provide fpgm)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/fpgm.js
|#
;; A list of instructions that are executed once when a font is first used.
;; These instructions are known as the font program. The main use of this table
;; is for the definition of functions that are used in many different glyph programs.
(define fpgm (x:struct 'instructions (x:array #:type uint8)))
(module+ test
(require rackunit racket/serialize "../helper.rkt")
(define ip (open-input-file charter-path))
(define dir (deserialize (read (open-input-file charter-directory-path))))
(define offset (hash-ref (hash-ref (hash-ref dir 'tables) 'fpgm) 'offset))
(define len (hash-ref (hash-ref (hash-ref dir 'tables) 'fpgm) 'length))
(check-equal? offset 4140)
(check-equal? len 371)
(check-equal? (pos ip 0) 0)
(check-equal? (hash-ref (decode fpgm (peek-bytes len offset ip)) 'instructions) '(184 0 0 44 75 184 0 9 80 88 177 1 1 142 89 184 1 255 133 184 0 68 29 185 0 9 0 3 95 94 45 184 0 1 44 32 32 69 105 68 176 1 96 45 184 0 2 44 184 0 1 42 33 45 184 0 3 44 32 70 176 3 37 70 82 88 35 89 32 138 32 138 73 100 138 32 70 32 104 97 100 176 4 37 70 32 104 97 100 82 88 35 101 138 89 47 32 176 0 83 88 105 32 176 0 84 88 33 176 64 89 27 105 32 176 0 84 88 33 176 64 101 89 89 58 45 184 0 4 44 32 70 176 4 37 70 82 88 35 138 89 32 70 32 106 97 100 176 4 37 70 32 106 97 100 82 88 35 138 89 47 253 45 184 0 5 44 75 32 176 3 38 80 88 81 88 176 128 68 27 176 64 68 89 27 33 33 32 69 176 192 80 88 176 192 68 27 33 89 89 45 184 0 6 44 32 32 69 105 68 176 1 96 32 32 69 125 105 24 68 176 1 96 45 184 0 7 44 184 0 6 42 45 184 0 8 44 75 32 176 3 38 83 88 176 64 27 176 0 89 138 138 32 176 3 38 83 88 35 33 176 128 138 138 27 138 35 89 32 176 3 38 83 88 35 33 184 0 192 138 138 27 138 35 89 32 176 3 38 83 88 35 33 184 1 0 138 138 27 138 35 89 32 176 3 38 83 88 35 33 184 1 64 138 138 27 138 35 89 32 184 0 3 38 83 88 176 3 37 69 184 1 128 80 88 35 33 184 1 128 35 33 27 176 3 37 69 35 33 35 33 89 27 33 89 68 45 184 0 9 44 75 83 88 69 68 27 33 33 89 45)))

@ -0,0 +1,20 @@
#lang racket/base
(require xenomorph)
(provide glyf)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/glyf.js
|#
(define glyf (x:array #:type (x:buffer)))
(module+ test
(require rackunit racket/serialize "../helper.rkt")
(define ip (open-input-file charter-path))
(define dir (deserialize (read (open-input-file charter-directory-path))))
(define offset (hash-ref (hash-ref (hash-ref dir 'tables) 'glyf) 'offset))
(define len (hash-ref (hash-ref (hash-ref dir 'tables) 'glyf) 'length))
(check-equal? offset 4620)
(check-equal? len 34072)
(file-position ip 0)
(define table-bytes (peek-bytes len offset ip)))

@ -0,0 +1,71 @@
#lang debug racket/base
(require xenomorph
racket/date)
(provide head)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/head.js
|#
; `name` table dates are seconds since 1/1/1904 (Mac file convention)
; whereas Racket / posix tracks since 1/1/1970
; so we adjust with the difference in seconds
(define mac-to-posix-delta 2082844800)
(define hfs-seconds (x:int #:size 8
#:signed #f
#:endian 'be
#:post-decode (λ (int) (seconds->date (- int mac-to-posix-delta) #f))
#:pre-encode (λ (dt) (+ (date->seconds dt #f) mac-to-posix-delta))))
(define head (x:struct
'version int32be ;; 0x00010000 (version 1.0)
'revision int32be ;; set by font manufacturer
'checkSumAdjustment uint32be
'magicNumber uint32be ;; set to 0x5F0F3CF5
'flags uint16be
'unitsPerEm uint16be ;; range from 64 to 16384
'created hfs-seconds
'modified hfs-seconds
'xMin int16be ;; for all glyph bounding boxes
'yMin int16be ;; for all glyph bounding boxes
'xMax int16be ;; for all glyph bounding boxes
'yMax int16be ;; for all glyph bounding boxes
'macStyle (x:bitfield #:type uint16be
#:flags '(bold italic underline outline shadow condensed extended))
'lowestRecPPEM uint16be ;; smallest readable size in pixels
'fontDirectionHint int16be
'indexToLocFormat int16be ;; 0 for short offsets 1 for long
'glyphDataFormat int16be ;; 0 for current format
))
(module+ test
(require rackunit "../helper.rkt"
racket/serialize)
(define ip (open-input-file charter-italic-path))
(define dir (deserialize (read (open-input-file charter-italic-directory-path))))
(define offset (hash-ref (hash-ref (hash-ref dir 'tables) 'head) 'offset))
(define length (hash-ref (hash-ref (hash-ref dir 'tables) 'head) 'length))
(check-equal? offset 236)
(check-equal? length 54)
(define table-bytes #"\0\1\0\0\0\2\0\0.\252t<_\17<\365\0\t\3\350\0\0\0\0\316\3\301\261\0\0\0\0\316\3\304\364\377\36\377\24\4\226\3\324\0\2\0\t\0\2\0\0\0\0")
(file-position ip 0)
(check-equal? (peek-bytes length offset ip) table-bytes)
(define table-data (decode head table-bytes))
(check-equal? (hash-ref table-data 'unitsPerEm) 1000)
(check-equal? (hash-ref table-data 'yMin) -236)
(check-equal? (hash-ref table-data 'yMax) 980)
(check-equal? (hash-ref table-data 'xMax) 1174)
(check-equal? (hash-ref table-data 'xMin) -226)
(check-equal? (hash-ref table-data 'macStyle) (make-hash '((shadow . #f)
(extended . #f)
(condensed . #f)
(underline . #f)
(outline . #f)
(bold . #f)
(italic . #t))))
(check-equal? (hash-ref table-data 'magicNumber) #x5F0F3CF5)
(check-equal? (hash-ref table-data 'indexToLocFormat) 0) ; used in loca table
(check-equal? (encode head table-data #f) table-bytes))

@ -0,0 +1,39 @@
#lang racket/base
(require xenomorph sugar/unstable/dict)
(provide hhea)
(define hhea (x:struct
(dictify
'version int32be
'ascent int16be ;; Distance from baseline of highest ascender
'descent int16be ;; Distance from baseline of lowest descender
'lineGap int16be ;; Typographic line gap
'advanceWidthMax uint16be ;; Maximum advance width value in 'hmtx' table
'minLeftSideBearing int16be ;; Maximum advance width value in 'hmtx' table
'minRightSideBearing int16be ;; Minimum right sidebearing value
'xMaxExtent int16be
'caretSlopeRise int16be ;; Used to calculate the slope of the cursor (rise/run); 1 for vertical
'caretSlopeRun int16be ;; 0 for vertical
'caretOffset int16be ;; Set to 0 for non-slanted fonts
'reserved (x:array #:type int16be #:length 4)
'metricDataFormat int16be ;; 0 for current format
'numberOfMetrics uint16be ;; Number of advance widths in 'hmtx' table
)))
(module+ test
(require rackunit "../helper.rkt"
racket/serialize)
(define ip (open-input-file charter-path))
(define dir (deserialize (read (open-input-file charter-directory-path))))
(define offset (hash-ref (hash-ref (hash-ref dir 'tables) 'hhea) 'offset))
(define length (hash-ref (hash-ref (hash-ref dir 'tables) 'hhea) 'length))
(check-equal? offset 292)
(check-equal? length 36)
(define table-bytes #"\0\1\0\0\3\324\377\22\0\0\4\311\377_\377`\4\251\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\345")
(file-position ip 0)
(check-equal? (peek-bytes length offset ip) table-bytes)
(define table-data (decode hhea table-bytes))
(check-equal? (hash-ref table-data 'ascent) 980)
(check-equal? (hash-ref table-data 'descent) -238)
(check-equal? (hash-ref table-data 'numberOfMetrics) 229))

@ -0,0 +1,35 @@
#lang racket/base
(require xenomorph)
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/hmtx.js
|#
(define hmtx-entry (x:struct 'advance uint16be 'bearing int16be))
(define hmtx (x:struct 'metrics (x:lazy-array #:type hmtx-entry
#:length (λ (arr) (hash-ref (hash-ref (hash-ref arr 'parent) 'hhea) 'numberOfMetrics)))
'bearings (x:lazy-array #:type int16be
#:length (λ (arr) (- (hash-ref (hash-ref (hash-ref arr 'parent) 'maxp) 'numGlyphs)
(hash-ref (hash-ref (hash-ref arr 'parent) 'hhea) 'numberOfMetrics))))))
(module+ test
(require rackunit racket/serialize racket/stream racket/class "../helper.rkt")
;; same as hmtx but doesn't require resolution of function to get length
(define hmtx-test (x:struct
'metrics (x:lazy-array hmtx-entry (λ (t) 229))
'bearing (x:lazy-array int16be (λ (t) 0))))
(define ip (open-input-file charter-path))
(define dir (deserialize (read (open-input-file charter-directory-path))))
(define hmtx-offset (hash-ref (hash-ref (hash-ref dir 'tables) 'hmtx) 'offset))
(define hmtx-length (hash-ref (hash-ref (hash-ref dir 'tables) 'hmtx) 'length))
(check-equal? hmtx-offset 456)
(check-equal? hmtx-length 916)
(define hmtx-bytes (peek-bytes hmtx-length hmtx-offset ip))
(define hmtx-data (decode hmtx-test hmtx-bytes))
(check-equal? (send hmtx-test x:size) (* 229 (send hmtx-entry x:size)))
(define H-gid 41) (define OE-gid 142)
(check-equal? (stream-ref (hash-ref hmtx-data 'metrics) H-gid) (make-hasheq '((bearing . 33) (advance . 738))))
(check-equal? (stream-ref (hash-ref hmtx-data 'metrics) OE-gid) (make-hasheq '((bearing . 43) (advance . 993)))))

@ -0,0 +1,66 @@
#lang debug racket/base
(require xenomorph
sugar/unstable/dict
racket/list
racket/promise
"../struct.rkt")
(provide loca loca-pre-encode loca-post-decode)
(define 16bit-style 0)
(define 32bit-style 1)
(define max-16-bit-value #xffff)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/loca.js
|#
(define (loca-pre-encode val)
(unless (hash-has-key? val x:version-key)
(hash-set! val x:version-key (if (> (last (hash-ref val 'offsets)) max-16-bit-value)
32bit-style
16bit-style))
(when (= 16bit-style (hash-ref val x:version-key))
(hash-update! val 'offsets (λ (offsets) (map (λ (x) (/ x 2)) offsets)))))
val)
(define (loca-post-decode val)
(when (= 16bit-style (hash-ref val x:version-key))
;; in a 16bits-style loca table, actual 32bit offset values are divided by 2 (to fit into 16 bits)
;; so we re-inflate them.
(hash-update! val 'offsets (λ (offsets) (map (λ (x) (* 2 x)) offsets))))
val)
(define loca (x:versioned-struct
#:pre-encode loca-pre-encode
#:post-decode loca-post-decode
;; todo: address ugliness to cross-ref head table from ttffont
(λ (o) (hash-ref (force (ttf-font-get-head-table-proc o)) 'indexToLocFormat))
(dictify
0 (dictify 'offsets (x:array #:type uint16be))
1 (dictify 'offsets (x:array #:type uint32be)))))
(define loca-v0 (x:versioned-struct
#:pre-encode loca-pre-encode
#:post-decode loca-post-decode
0
(dictify
0 (dictify 'offsets (x:array #:type uint16be))
1 (dictify 'offsets (x:array #:type uint32be)))))
(module+ test
(require rackunit racket/serialize "../helper.rkt")
(check-equal?
(encode loca (mhasheq x:version-key 0 'offsets '(0 76 156)) #f) #"\0\0\0L\0\234")
(check-equal?
(encode loca (mhasheq x:version-key 1 'offsets '(0 76 156)) #f) #"\0\0\0\0\0\0\0L\0\0\0\234")
(define ip (open-input-file charter-path))
(define dir (deserialize (read (open-input-file charter-directory-path))))
(define offset (hash-ref (hash-ref (hash-ref dir 'tables) 'loca) 'offset))
(define len (hash-ref (hash-ref (hash-ref dir 'tables) 'loca) 'length))
(check-equal? offset 38692)
(check-equal? len 460)
(define offset-bytes (peek-bytes len offset ip))
(define offsets (map (λ (x) (* 2 x)) (decode (x:array uint16be) offset-bytes)))
(check-equal? (length offsets) 230)
(check-equal? offsets '(0 0 0 136 296 500 864 1168 1548 1628 1716 1804 1944 2048 2128 2176 2256 2312 2500 2596 2788 3052 3168 3396 3624 3732 4056 4268 4424 4564 4640 4728 4804 5012 5384 5532 5808 6012 6212 6456 6672 6916 7204 7336 7496 7740 7892 8180 8432 8648 8892 9160 9496 9764 9936 10160 10312 10536 10780 10992 11148 11216 11272 11340 11404 11444 11524 11820 12044 12216 12488 12728 12932 13324 13584 13748 13924 14128 14232 14592 14852 15044 15336 15588 15776 16020 16164 16368 16520 16744 16984 17164 17320 17532 17576 17788 17896 18036 18284 18552 18616 18988 19228 19512 19712 19796 19976 20096 20160 20224 20536 20836 20876 21000 21200 21268 21368 21452 21532 21720 21908 22036 22244 22664 22872 22932 22992 23088 23220 23268 23372 23440 23600 23752 23868 23988 24084 24184 24224 24548 24788 25012 25292 25716 25884 26292 26396 26540 26796 27172 27488 27512 27536 27560 27584 27912 27936 27960 27984 28008 28032 28056 28080 28104 28128 28152 28176 28200 28224 28248 28272 28296 28320 28344 28368 28392 28416 28440 28464 28488 28512 28536 28560 28968 28992 29016 29040 29064 29088 29112 29136 29160 29184 29208 29232 29256 29280 29304 29328 29352 29376 29400 29424 29448 29472 29496 29520 29824 30164 30220 30652 30700 30956 31224 31248 31332 31488 31636 31916 32104 32176 32484 32744 32832 32956 33248 33664 33884 34048 34072)))

@ -0,0 +1,36 @@
#lang racket/base
(require xenomorph)
(provide maxp)
(define maxp (x:struct
'version int32be
'numGlyphs uint16be ;; The number of glyphs in the font
'maxPoints uint16be ;; Maximum points in a non-composite glyph
'maxContours uint16be ;; Maximum contours in a non-composite glyph
'maxComponentPoints uint16be ;; Maximum points in a composite glyph
'maxComponentContours uint16be ;; Maximum contours in a composite glyph
'maxZones uint16be ;; 1 if instructions do not use the twilight zone, 2 otherwise
'maxTwilightPoints uint16be ;; Maximum points used in Z0
'maxStorage uint16be ;; Number of Storage Area locations
'maxFunctionDefs uint16be ;; Number of FDEFs
'maxInstructionDefs uint16be ;; Number of IDEFs
'maxStackElements uint16be ;; Maximum stack depth
'maxSizeOfInstructions uint16be ;; Maximum byte count for glyph instructions
'maxComponentElements uint16be ;; Maximum number of components referenced at “top level” for any composite glyph
'maxComponentDepth uint16be ;; Maximum levels of recursion; 1 for simple components
))
(module+ test
(require rackunit racket/serialize "../helper.rkt")
(define ip (open-input-file charter-path))
(define dir (deserialize (read (open-input-file charter-directory-path))))
(define maxp-offset (hash-ref (hash-ref (hash-ref dir 'tables) 'maxp) 'offset))
(define maxp-length (hash-ref (hash-ref (hash-ref dir 'tables) 'maxp) 'length))
(check-equal? maxp-offset 328)
(check-equal? maxp-length 32)
(define maxp-bytes #"\0\1\0\0\0\345\0f\0\a\0O\0\4\0\1\0\0\0\0\0\n\0\0\2\0\1s\0\2\0\1")
(file-position ip 0)
(check-equal? (peek-bytes maxp-length maxp-offset ip) maxp-bytes)
(define maxp-data (decode maxp maxp-bytes))
(check-equal? (hash-ref maxp-data 'numGlyphs) 229)
(check-equal? (hash-ref maxp-data 'version) 65536))

@ -0,0 +1,40 @@
#lang racket/base
(require xenomorph
sugar/unstable/dict)
(provide post)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/post.js
|#
(define post (x:versioned-struct
fixed32be
(dictify
'header (dictify 'italicAngle fixed32be ;; Italic angle in counter-clockwise degrees from the vertical.
'underlinePosition int16be ;; Suggested distance of the top of the underline from the baseline
'underlineThickness int16be ;; Suggested values for the underline thickness
'isFixedPitch uint32be ;; Whether the font is monospaced
'minMemType42 uint32be ;; Minimum memory usage when a TrueType font is downloaded as a Type 42 font
'maxMemType42 uint32be ;; Maximum memory usage when a TrueType font is downloaded as a Type 42 font
'minMemType1 uint32be ;; Minimum memory usage when a TrueType font is downloaded as a Type 1 font
'maxMemType1 uint32be) ;; Maximum memory usage when a TrueType font is downloaded as a Type 1 font
1 null
2 (dictify 'numberOfGlyphs uint16be
'glyphNameIndex (x:array #:type uint16be #:length (λ (p) (hash-ref p 'numberOfGlyphs)))
'names (x:array (x:string #:length uint8))
)
2.5 (dictify 'numberOfGlyphs uint16be
'offsets (x:array #:type uint8))
3 null
4 (dictify 'map (x:array #:type uint32be #:length (λ (t) (hash-ref (hash-ref (hash-ref t 'parent) 'maxp) 'numGlyphs)))))))
(module+ test
(require rackunit racket/serialize racket/class "../helper.rkt")
(define ip (open-input-file charter-path))
(define dir (deserialize (read (open-input-file charter-directory-path))))
(define offset (hash-ref (hash-ref (hash-ref dir 'tables) 'post) 'offset))
(define len (hash-ref (hash-ref (hash-ref dir 'tables) 'post) 'length))
(check-equal? offset 41520)
(check-equal? len 514))

@ -0,0 +1,24 @@
#lang racket/base
(require sugar/unstable/dict xenomorph)
(provide prep)
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/prep.js
|#
(define prep (x:struct 'controlValueProgram (x:array #:type uint8)))
(module+ test
(require rackunit racket/dict racket/serialize "../helper.rkt")
(define ip (open-input-file charter-path))
(define dir (deserialize (read (open-input-file charter-directory-path))))
(define offset (hash-ref (hash-ref (hash-ref dir 'tables) 'prep) 'offset))
(define len (hash-ref (hash-ref (hash-ref dir 'tables) 'prep) 'length))
(check-equal? offset 4512)
(check-equal? len 78)
(file-position ip 0)
(define table-bytes #"\270\0\0+\0\272\0\1\0\1\0\2+\1\272\0\2\0\1\0\2+\1\277\0\2\0C\0007\0+\0\37\0\23\0\0\0\b+\0\277\0\1\0\200\0i\0R\0;\0#\0\0\0\b+\0\272\0\3\0\5\0\a+\270\0\0 E}i\30D")
(check-equal? table-bytes (peek-bytes len offset ip))
(define ds (open-input-bytes (peek-bytes len offset ip)))
(check-equal? (dict-ref (decode prep ds) 'controlValueProgram) '(184 0 0 43 0 186 0 1 0 1 0 2 43 1 186 0 2 0 1 0 2 43 1 191 0 2 0 67 0 55 0 43 0 31 0 19 0 0 0 8 43 0 191 0 1 0 128 0 105 0 82 0 59 0 35 0 0 0 8 43 0 186 0 3 0 5 0 7 43 184 0 0 32 69 125 105 24 68)))

@ -0,0 +1,29 @@
#lang racket/base
(require (for-syntax racket/base racket/string) "helper.rkt")
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/fontkit/blob/master/src/tables/index.js
|#
(define-syntax (define-table-codecs stx)
(syntax-case stx ()
[(_ ID TABLE-ID ...)
(with-syntax ([(TABLE-ID-STRING ...) (map (λ (s) (datum->syntax stx (string-append "table/" (string-replace (format "~a.rkt" (syntax->datum s)) "/" ""))))
(syntax->list #'(TABLE-ID ...)))])
#'(begin
(r+p TABLE-ID-STRING ...)
(define ID (make-hasheq (map cons (list 'TABLE-ID ...) (list TABLE-ID ...))))))]))
(define-table-codecs table-codecs head hhea hmtx maxp OS/2 post cvt_ fpgm loca prep glyf CFF_)
#|
Tables not supported:
cmap name
PostScript outlines:
CFF2 VORG
Advanced OpenType Tables
BASE GDEF GPOS GSUB JSTF
|#

@ -0,0 +1,93 @@
Copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -0,0 +1,94 @@
Copyright (c) 2010, Danh Hong (khmertype.blogspot.com),
with Reserved Font Name Khmer.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -0,0 +1,87 @@
Copyright © 2015-2016 The Mada Project Authors, with Reserved Font Name "Source". Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
---------------------------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
---------------------------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development
of collaborative font projects, to support the font creation efforts of academic
and linguistic communities, and to provide a free and open framework in which
fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and redistributed
freely as long as they are not sold by themselves. The fonts, including any
derivative works, can be bundled, embedded, redistributed and/or sold with any
software provided that any reserved names are not used by derivative works. The
fonts and derivatives, however, cannot be released under any other type of license.
The requirement for fonts to remain under this license does not apply to any
document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright Holder(s) under
this license and clearly marked as such. This may include source files, build
scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the copyright
statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting, or
substituting -- in part or in whole -- any of the components of the Original Version,
by changing formats or by porting the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical writer or other
person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of the
Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell
modified and unmodified copies of the Font Software, subject to the following
conditions:
1) Neither the Font Software nor any of its individual components, in Original or
Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled, redistributed
and/or sold with any software, provided that each copy contains the above copyright
notice and this license. These can be included either as stand-alone text files,
human-readable headers or in the appropriate machine-readable metadata fields within
text or binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless
explicit written permission is granted by the corresponding Copyright Holder. This
restriction only applies to the primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall
not be used to promote, endorse or advertise any Modified Version, except to
acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with
their explicit written permission.
5) The Font Software, modified or unmodified, in part or in whole, must be distributed
entirely under this license, and must not be distributed under any other license. The
requirement for fonts to remain under this license does not apply to any document
created using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER
RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR
INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@ -0,0 +1,92 @@
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -0,0 +1,94 @@
Copyright (c) 2011, Jonas Hecksher, Playtypes, e-types AS (e-types.com),
with Reserved Font Name "Play", "Playtype", "Playtype Sans".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -0,0 +1,93 @@
Copyright (c) 2010-2013 by Claus Eggers Sørensen (es@forthehearts.net), with Reserved Font Name 'Playfair'
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -0,0 +1,3 @@
The fonts in this directory are free and open source fonts used for testing purposes.
The goal is to test as much of fontkit's functionality as possible. The licenses for these
fonts are included in the subfolders for each family, along with the font files themselves.

@ -0,0 +1,93 @@
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save