├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── fontproof ├── command.rkt ├── info.rkt └── main.rkt ├── info.rkt ├── qtest ├── all.rkt ├── demo-footnotes.rkt ├── docs.rkt ├── fark.rkt ├── fonts │ ├── arbitrary-name │ │ └── my-name-does-not-matter.woff │ └── charter │ │ └── charter.woff ├── hyphenate.md ├── info.rkt ├── log.rkt ├── mds │ ├── apply.md │ ├── begin.md │ ├── binding.md │ ├── booleans.md │ ├── boxes.md │ ├── byte-strings.md │ ├── case.md │ ├── char-strings.md │ ├── chars.md │ ├── class.md │ ├── cmdline.md │ ├── compile.md │ ├── concurrency.md │ ├── cond.md │ ├── contracts.md │ ├── control.md │ ├── data.md │ ├── define-struct.md │ ├── define.md │ ├── dialects.md │ ├── distributed.md │ ├── for.md │ ├── forms.md │ ├── futures.md │ ├── graphics.md │ ├── guide.md │ ├── hash-languages.md │ ├── hash-tables.md │ ├── io.md │ ├── keywords.md │ ├── lambda.md │ ├── languages.md │ ├── let.md │ ├── lists.md │ ├── macro-module.md │ ├── macros.md │ ├── match.md │ ├── module-basics.md │ ├── module-languages.md │ ├── module-macro.md │ ├── module-paths.md │ ├── module-provide.md │ ├── module-require.md │ ├── module-set.md │ ├── module-syntax.md │ ├── modules.md │ ├── named-let.md │ ├── namespaces.md │ ├── numbers.md │ ├── other-editors.md │ ├── other.md │ ├── pairs.md │ ├── parallelism.md │ ├── parameterize.md │ ├── paths.md │ ├── pattern-macros.md │ ├── performance.md │ ├── phases.md │ ├── places.md │ ├── ports.md │ ├── proc-macros.md │ ├── qq.md │ ├── quote.md │ ├── reader-extension.md │ ├── regexp.md │ ├── regexps-data.md │ ├── running.md │ ├── scripts.md │ ├── set.md │ ├── simple-data.md │ ├── simple-syntax.md │ ├── symbols.md │ ├── syntax-taints.md │ ├── to-scheme.md │ ├── truth.md │ ├── unit.md │ ├── vectors.md │ ├── void-and-undef.md │ └── welcome.md ├── mydraw.rkt ├── paths-to-test.rkt ├── raco.rkt ├── sample-main.rkt ├── test-adjustment-sizing-tester.pdf ├── test-adjustment-sizing.rkt ├── test-baseline-shift-tester.pdf ├── test-baseline-shift.rkt ├── test-breaks-tester.pdf ├── test-breaks.rkt ├── test-docs-tester.pdf ├── test-docs.rkt ├── test-emoji-tester.pdf ├── test-emoji.rkt ├── test-fallback-mini-tester.pdf ├── test-fallback-mini.rkt ├── test-fallback-super-tester.pdf ├── test-fallback-super.rkt ├── test-fancy-sauce-tester.pdf ├── test-fancy-sauce.rkt ├── test-font-setup-tester.pdf ├── test-font-setup.rkt ├── test-font-tracking-tester.pdf ├── test-font-tracking.rkt ├── test-hello-tester.pdf ├── test-hello.rkt ├── test-image-tester.pdf ├── test-image.rkt ├── test-kafka-tester.pdf ├── test-kafka.rkt ├── test-keep-with-next-tester.pdf ├── test-keep-with-next.rkt ├── test-metadata-tester.pdf ├── test-metadata.rkt ├── test-ot-features-tester.pdf ├── test-ot-features.rkt ├── test-sections-tester.pdf ├── test-sections.rkt ├── test-symbol-tester.pdf ├── test-symbol.rkt ├── test.jpeg ├── test.png └── update-tests.rkt ├── quad ├── atomize.rkt ├── base.rkt ├── checksum.rkt ├── doclang-raw.rkt ├── get-info.rkt ├── info.rkt ├── lang.rkt ├── log.rkt ├── main.rkt ├── ocm.rkt ├── param.rkt ├── pict.rkt ├── position.rkt ├── qexpr.rkt ├── quad.rkt ├── reader.rkt ├── rebase.rkt ├── scribblings │ ├── pollen-example │ │ ├── pollen.rkt │ │ ├── template.pdf.p │ │ └── test.pdf.pm │ ├── quad.scrbl │ └── quads.png ├── unicode │ ├── LICENSE │ ├── emoji.rkt │ ├── math.rkt │ └── unicode-class-prep.rkt ├── util.rkt └── wrap.rkt ├── quad2 └── main.rkt └── quadwriter ├── attrs.rkt ├── block.rkt ├── break.rkt ├── column.rkt ├── core.rkt ├── debug.rkt ├── doc.rkt ├── draw.rkt ├── font.rkt ├── fonts ├── blockquote │ ├── LICENSE.txt │ ├── bold-italic │ │ └── fira-sans-bold-italic.otf │ ├── bold │ │ └── fira-sans-bold.otf │ ├── italic │ │ └── fira-sans-italic.otf │ └── regular │ │ └── fira-sans.otf ├── code │ ├── LICENSE.txt │ └── fira-mono.otf ├── default │ └── SourceSerifPro-Regular.otf ├── fallback-emoji │ ├── LICENSE_OFL.txt │ ├── NotoEmoji-Regular.ttf │ └── README ├── fallback-math │ ├── LICENSE_OFL.txt │ └── NotoSansMath-Regular.ttf ├── fallback │ ├── LICENSE_OFL.txt │ ├── bold-italic │ │ └── NotoSans-BoldItalic.ttf │ ├── bold │ │ └── NotoSans-Bold.ttf │ ├── italic │ │ └── NotoSans-Italic.ttf │ └── regular │ │ └── NotoSans-Regular.ttf ├── heading │ ├── LICENSE.txt │ ├── bold-italic │ │ └── fira-sans-light-bold-italic.otf │ ├── bold │ │ └── fira-sans-light-bold.otf │ ├── italic │ │ └── fira-sans-light-italic.otf │ └── regular │ │ └── fira-sans-light.otf └── text │ ├── LICENSE.md │ ├── bold-italic │ └── SourceSerifPro-BoldIt.otf │ ├── bold │ └── SourceSerifPro-Bold.otf │ ├── italic │ └── SourceSerifPro-It.otf │ └── regular │ └── SourceSerifPro-Regular.otf ├── html.rkt ├── image.rkt ├── keep.rkt ├── lang-helper.rkt ├── lang.rkt ├── line.rkt ├── log.rkt ├── main.rkt ├── markdown.rkt ├── markup.rkt ├── page.rkt ├── para.rkt ├── param.rkt ├── query.rkt ├── render.rkt ├── section.rkt ├── string.rkt ├── struct.rkt ├── tags.rkt └── test.rkt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://mbtype.com 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | run: 7 | name: "Build using Racket '${{ matrix.racket-version }}' (${{ matrix.racket-variant }})" 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | racket-version: ["7.1", "7.2", "7.3", "7.4", "7.5", "7.6", "7.7", "7.8", "7.9", "current"] 13 | racket-variant: ["BC", "CS"] 14 | # CS builds are only provided for versions 7.4 and up so avoid 15 | # running the job for prior versions. 16 | exclude: 17 | - {racket-version: "7.1", racket-variant: "CS"} 18 | - {racket-version: "7.2", racket-variant: "CS"} 19 | - {racket-version: "7.3", racket-variant: "CS"} 20 | 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@master 24 | 25 | - uses: Bogdanp/setup-racket@v0.11 26 | with: 27 | distribution: 'full' 28 | version: ${{ matrix.racket-version }} 29 | variant: ${{ matrix.racket-variant }} 30 | 31 | - name: Install package and its dependencies 32 | run: raco pkg install --auto --batch 33 | 34 | - name: Start virtual framebuffer 35 | run: Xvfb "$DISPLAY" -screen 0 1280x1024x24 & 36 | 37 | - name: Run the tests 38 | run: raco test -j 4 -p quad 39 | 40 | - name: Run the qtests 41 | run: racket -l qtest/all 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # for Racket 2 | compiled/ 3 | 4 | # for Mac OS X 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | Icon 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear on external disk 14 | .Spotlight-V100 15 | .Trashes 16 | 17 | 18 | *logger.txt 19 | *log.txt 20 | *.cache 21 | quad/scribblings/manual-fonts.css 22 | quad/scribblings/manual-racket.css 23 | quad/scribblings/manual-racket.js 24 | quad/scribblings/manual-style.css 25 | quad/scribblings/quad.html 26 | quad/scribblings/racket.css 27 | quad/scribblings/scribble-common.js 28 | quad/scribblings/scribble.css 29 | quad/scribblings/pict*.png 30 | quad/scribblings/mb.css 31 | quad/doc 32 | *.pdf 33 | !*tester.pdf 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License for Quad (code only) 2 | 3 | © 2014-2019 Matthew Butterick 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Quad has moved 2 | 3 | This repo is obsolete. It has been superseded by [`mbutterick/typesetting`](https://github.com/mbutterick/typesetting), which is now the main repo for Quad (and other related projects). All issues that were pending on `quad` have been transferred there. At some point in 2022 this repo will be deleted. 4 | -------------------------------------------------------------------------------- /fontproof/command.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require racket/logging 3 | racket/match 4 | "main.rkt") 5 | 6 | (module+ raco 7 | (with-logging-to-port 8 | (current-error-port) 9 | handle-raco-command 10 | #:logger fontproof-logger 11 | 'info 12 | 'fontproof)) 13 | 14 | (module+ main 15 | (println "this is fontproof command")) 16 | 17 | (define (handle-raco-command) 18 | (define command-name (with-handlers ([exn:fail? (λ (exn) #f)]) 19 | (vector-ref (current-command-line-arguments) 0))) 20 | (define output-file-path #false) 21 | (define page-size #false) 22 | (define font-sizes #false) 23 | (define line-heights #false) 24 | (define doc #false) 25 | (define replace #false) 26 | (define output-qml? #false) 27 | (define make-bold? #false) 28 | (define make-italic? #false) 29 | (define query-mode? #false) 30 | (define families 31 | (command-line 32 | #:program "fontproof" 33 | #:argv (current-command-line-arguments) 34 | #:once-each 35 | [("-p" "--page") page-size-arg 36 | "page size (e.g., letter, A4)" 37 | (set! page-size page-size-arg)] 38 | [("-r" "--replace") "replace existing proof with same name" 39 | (set! replace #true)] 40 | [("-d" "--doc") doc-arg 41 | "source for sample text" 42 | (set! doc doc-arg)] 43 | [("-o" "--output") output-file-path-arg 44 | "output file path" 45 | (set! output-file-path output-file-path-arg)] 46 | [("-s" "--size") font-sizes-arg 47 | "font sizes to proof" 48 | (set! font-sizes font-sizes-arg)] 49 | [("-l" "--leading") line-heights-arg 50 | "line height" 51 | (set! line-heights line-heights-arg)] 52 | [("-b" "--bold") "also generate bold proof (only works with family name)" 53 | (set! make-bold? #true)] 54 | [("-i" "--italic") "also generate italic proof (only works with family name)" 55 | (set! make-italic? #true)] 56 | [("-q" "--qml") "output QML file" 57 | (set! output-qml? #true)] 58 | [("--query") "resolve each family name and font path and show status" 59 | (set! query-mode? #true)] 60 | #:args font-family-names-or-font-paths 61 | font-family-names-or-font-paths)) 62 | (match families 63 | [(? null?) (raise-user-error "no fonts to proof; exiting")] 64 | [_ (for ([family (in-list families)]) 65 | (cond 66 | [query-mode? 67 | (define status 68 | (match family 69 | [(? font-path-string? ps) 70 | (format (if (file-exists? ps) "found at ~v" "not found at ~v") (path->string (path->complete-path ps)))] 71 | [_ (match-define-values (trimmed-family bold? italic?) (resolve-family-bold-italic family)) 72 | (match ((dynamic-require 'fontland/font-path 'family->path) trimmed-family #:bold bold? #:italic italic?) 73 | [#false "not found among installed fonts"] 74 | [(app path->string ps) (format "found installed at ~v" ps)])])) 75 | (log-info (format "family ~v ~a" family status))] 76 | [else 77 | (make-proof family 78 | (or doc (match (current-input-port) 79 | ;; pull text out of stdin, if any 80 | ;; use `terminal-port?` to distinguish piped input from tty input 81 | [(not (? terminal-port?)) 82 | (string-join (for/list ([t (in-port read)]) 83 | (format "~a" t)) " ")] 84 | [_ #false])) 85 | #:page-size page-size 86 | #:bold make-bold? 87 | #:italic make-italic? 88 | #:font-sizes font-sizes 89 | #:line-heights line-heights 90 | #:output-file-path output-file-path 91 | #:replace replace 92 | #:qml output-qml?)]))])) 93 | 94 | 95 | -------------------------------------------------------------------------------- /fontproof/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define raco-commands '(("fontproof" (submod fontproof/command raco) "issue fontproof command" #f))) 4 | -------------------------------------------------------------------------------- /info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | (define collection 'multi) 3 | (define deps '("at-exp-lib" 4 | ["base" #:version "7.1"] 5 | "beautiful-racket-lib" 6 | "fontland" 7 | "hyphenate" 8 | "pitfall" 9 | "pollen" 10 | "rackunit-lib" 11 | "sugar" 12 | "txexpr" 13 | "markdown" 14 | "pict-lib" 15 | "debug" 16 | "words")) 17 | (define build-deps '("draw-lib" 18 | "draw-doc" 19 | "racket-doc" 20 | "scribble-lib")) 21 | (define update-implies '("fontland" "pitfall" "words")) -------------------------------------------------------------------------------- /qtest/all.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require quadwriter 3 | pitfall/check-pdf 4 | racket/runtime-path 5 | "paths-to-test.rkt") 6 | 7 | ;; `racket -l qtest/all` 8 | 9 | (for ([test-path (in-list (find-test-paths))]) 10 | (define pdf-path (path-replace-extension test-path #".pdf")) 11 | (define-values (dir name _) (split-path test-path)) 12 | (displayln (path->string name)) 13 | (check-pdfs-equal? (time (parameterize ([quadwriter-test-mode #t] 14 | [current-output-port (open-output-nowhere)]) 15 | (render-pdf (dynamic-require test-path 'doc) pdf-path test-path) 16 | pdf-path)) 17 | (test-pdf-name test-path))) -------------------------------------------------------------------------------- /qtest/demo-footnotes.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter 2 | 3 | #:page-height "8in" 4 | #:page-width "6in" 5 | 6 | '(q ((flow "footnote")(fn-text "0")(font-size-adjust "80%")(line-heightr-adjust "70%")) "Leftover from previous footnote." (q ((break "para")))) 7 | 8 | "Hello" '(q ((fn-ref "1")) "*") 9 | 10 | '(q ((flow "footnote")(fn-text "1")(font-size-adjust "80%")(line-height-adjust "70%")) (q ((fn-text-start "1")) "*") "A convertible value in the sense of convertible? is used in a renderer-specific way, but values convertible to 'text renders the same as the resulting string. If a renderer is not able to convert the value to a known format, the value is converted to a string using write." (q ((break "para")))) 11 | 12 | " world." '(q ((fn-ref "2")) "†") 13 | 14 | '(q ((flow "footnote")(fn-text "2")(font-size-adjust "80%")(line-height-adjust "70%")) (q ((fn-text-start "2")) "†") "An instance of link-element has a tag for the target of the link." (q ((break "para")))) 15 | 16 | " I love you." -------------------------------------------------------------------------------- /qtest/fonts/arbitrary-name/my-name-does-not-matter.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/fonts/arbitrary-name/my-name-does-not-matter.woff -------------------------------------------------------------------------------- /qtest/fonts/charter/charter.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/fonts/charter/charter.woff -------------------------------------------------------------------------------- /qtest/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | (define compile-omit-paths 'all) 3 | (define test-omit-paths 'all) 4 | (define raco-commands '(("qtest" qtest/raco "qtests" #f))) -------------------------------------------------------------------------------- /qtest/log.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/logging) 3 | (provide (all-defined-out)) 4 | (define-logger qtest) -------------------------------------------------------------------------------- /qtest/mds/binding.md: -------------------------------------------------------------------------------- 1 | # Identifiers and Binding 2 | 3 | The context of an expression determines the meaning of identifiers that 4 | appear in the expression. In particular, starting a module with the 5 | language `racket`, as in 6 | 7 | `#lang` `racket` 8 | 9 | means that, within the module, the identifiers described in this guide 10 | start with the meaning described here: `cons` refers to the function 11 | that creates a pair, `car` refers to the function that extracts the 12 | first element of a pair, and so on. 13 | 14 | > +\[missing\] introduces the syntax of identifiers. 15 | 16 | Forms like `define`, `lambda`, and `let` associate a meaning with one or 17 | more identifiers; that is, they _bind_ identifiers. The part of the 18 | program for which the binding applies is the _scope_ of the binding. The 19 | set of bindings in effect for a given expression is the expression’s 20 | _environment_. 21 | 22 | For example, in 23 | 24 | ```racket 25 | #lang racket 26 | 27 | (define f 28 | (lambda (x) 29 | (let ([y 5]) 30 | (+ x y)))) 31 | 32 | (f 10) 33 | ``` 34 | 35 | the `define` is a binding of `f`, the `lambda` has a binding for `x`, 36 | and the `let` has a binding for `y`. The scope of the binding for `f` is 37 | the entire module; the scope of the `x` binding is `(let ([y 5]) (+ x 38 | y))`; and the scope of the `y` binding is just `(+ x y)`. The 39 | environment of `(+ x y)` includes bindings for `y`, `x`, and `f`, as 40 | well as everything in `racket`. 41 | 42 | A module-level `define` can bind only identifiers that are not already 43 | defined or `require`d into the module. A local `define` or other binding 44 | forms, however, can give a new local binding for an identifier that 45 | already has a binding; such a binding _shadows_ the existing binding. 46 | 47 | Examples: 48 | 49 | ```racket 50 | (define f 51 | (lambda (append) 52 | (define cons (append "ugly" "confusing")) 53 | (let ([append 'this-was]) 54 | (list append cons)))) 55 | 56 | > (f list) 57 | '(this-was ("ugly" "confusing")) 58 | ``` 59 | 60 | Similarly, a module-level `define` can shadow a binding from the 61 | module’s language. For example, `(define cons 1)` in a `racket` module 62 | shadows the `cons` that is provided by `racket`. Intentionally shadowing 63 | a language binding is rarely a good idea—especially for widely used 64 | bindings like `cons`—but shadowing relieves a programmer from having to 65 | avoid every obscure binding that is provided by a language. 66 | 67 | Even identifiers like `define` and `lambda` get their meanings from 68 | bindings, though they have _transformer_ bindings \(which means that 69 | they indicate syntactic forms\) instead of value bindings. Since 70 | `define` has a transformer binding, the identifier `define` cannot be 71 | used by itself to get a value. However, the normal binding for `define` 72 | can be shadowed. 73 | 74 | Examples: 75 | 76 | ```racket 77 | > define 78 | eval:1:0: define: bad syntax 79 | in: define 80 | > (let ([define 5]) define) 81 | 5 82 | ``` 83 | 84 | Again, shadowing standard bindings in this way is rarely a good idea, 85 | but the possibility is an inherent part of Racket’s flexibility. 86 | -------------------------------------------------------------------------------- /qtest/mds/booleans.md: -------------------------------------------------------------------------------- 1 | # Booleans 2 | 3 | Racket has two distinguished constants to represent boolean values: `#t` 4 | for true and `#f` for false. Uppercase `#T` and `#F` are parsed as the 5 | same values, but the lowercase forms are preferred. 6 | 7 | The `boolean?` procedure recognizes the two boolean constants. In the 8 | result of a test expression for `if`, `cond`, `and`, `or`, etc., 9 | however, any value other than `#f` counts as true. 10 | 11 | Examples: 12 | 13 | ```racket 14 | > (= 2 (+ 1 1)) 15 | #t 16 | > (boolean? #t) 17 | #t 18 | > (boolean? #f) 19 | #t 20 | > (boolean? "no") 21 | #f 22 | > (if "no" 1 0) 23 | 1 24 | ``` 25 | -------------------------------------------------------------------------------- /qtest/mds/boxes.md: -------------------------------------------------------------------------------- 1 | # Boxes 2 | 3 | A _box_ is like a single-element vector. It can print as a quoted `#&` 4 | followed by the printed form of the boxed value. A `#&` form can also be 5 | used as an expression, but since the resulting box is constant, it has 6 | practically no use. 7 | 8 | Examples: 9 | 10 | ```racket 11 | > (define b (box "apple")) 12 | > b 13 | '#&"apple" 14 | > (unbox b) 15 | "apple" 16 | > (set-box! b '(banana boat)) 17 | > b 18 | '#&(banana boat) 19 | ``` 20 | 21 | > +\[missing\] in \[missing\] provides more on boxes and box procedures. 22 | -------------------------------------------------------------------------------- /qtest/mds/byte-strings.md: -------------------------------------------------------------------------------- 1 | # Bytes and Byte Strings 2 | 3 | A _byte_ is an exact integer between `0` and `255`, inclusive. The 4 | `byte?` predicate recognizes numbers that represent bytes. 5 | 6 | Examples: 7 | 8 | ```racket 9 | > (byte? 0) 10 | #t 11 | > (byte? 256) 12 | #f 13 | ``` 14 | 15 | A _byte string_ is similar to a string—see \[missing\]—but its content 16 | is a sequence of bytes instead of characters. Byte strings can be used 17 | in applications that process pure ASCII instead of Unicode text. The 18 | printed form of a byte string supports such uses in particular, because 19 | a byte string prints like the ASCII decoding of the byte string, but 20 | prefixed with a `#`. Unprintable ASCII characters or non-ASCII bytes in 21 | the byte string are written with octal notation. 22 | 23 | > +\[missing\] in \[missing\] documents the fine points of the syntax of 24 | > byte strings. 25 | 26 | Examples: 27 | 28 | ```racket 29 | > #"Apple" 30 | #"Apple" 31 | > (bytes-ref #"Apple" 0) 32 | 65 33 | > (make-bytes 3 65) 34 | #"AAA" 35 | > (define b (make-bytes 2 0)) 36 | > b 37 | #"\0\0" 38 | > (bytes-set! b 0 1) 39 | > (bytes-set! b 1 255) 40 | > b 41 | #"\1\377" 42 | ``` 43 | 44 | The `display` form of a byte string writes its raw bytes to the current 45 | output port \(see \[missing\]\). Technically, `display` of a normal 46 | \(i.e,. character\) string prints the UTF-8 encoding of the string to 47 | the current output port, since output is ultimately defined in terms of 48 | bytes; `display` of a byte string, however, writes the raw bytes with no 49 | encoding. Along the same lines, when this documentation shows output, it 50 | technically shows the UTF-8-decoded form of the output. 51 | 52 | Examples: 53 | 54 | ```racket 55 | > (display #"Apple") 56 | Apple 57 | > (display "\316\273") ; same as "λ" 58 | λ 59 | > (display #"\316\273") ; UTF-8 encoding of λ 60 | λ 61 | ``` 62 | 63 | For explicitly converting between strings and byte strings, Racket 64 | supports three kinds of encodings directly: UTF-8, Latin-1, and the 65 | current locale’s encoding. General facilities for byte-to-byte 66 | conversions \(especially to and from UTF-8\) fill the gap to support 67 | arbitrary string encodings. 68 | 69 | Examples: 70 | 71 | ```racket 72 | > (bytes->string/utf-8 #"\316\273") 73 | "λ" 74 | > (bytes->string/latin-1 #"\316\273") 75 | "λ" 76 | > (parameterize ([current-locale "C"]) ; C locale supports ASCII, 77 | (bytes->string/locale #"\316\273")) ; only, so... 78 | bytes->string/locale: byte string is not a valid encoding 79 | for the current locale 80 | byte string: #"\316\273" 81 | > (let ([cvt (bytes-open-converter "cp1253" ; Greek code page 82 | "UTF-8")] 83 | [dest (make-bytes 2)]) 84 | (bytes-convert cvt #"\353" 0 1 dest) 85 | (bytes-close-converter cvt) 86 | (bytes->string/utf-8 dest)) 87 | "λ" 88 | ``` 89 | 90 | > +\[missing\] in \[missing\] provides more on byte strings and 91 | > byte-string procedures. 92 | -------------------------------------------------------------------------------- /qtest/mds/case.md: -------------------------------------------------------------------------------- 1 | # Simple Dispatch: `case` 2 | 3 | The `case` form dispatches to a clause by matching the result of an 4 | expression to the values for the clause: 5 | 6 | ```racket 7 | (case expr 8 | [(datum ...+) body ...+] 9 | ...) 10 | ``` 11 | 12 | Each `datum` will be compared to the result of `expr` using `equal?`, 13 | and then the corresponding `body`s are evaluated. The `case` form can 14 | dispatch to the correct clause in _O_\(_log N_\)__ time for _N_ 15 | `datum`s. 16 | 17 | Multiple `datum`s can be supplied for each clause, and the corresponding 18 | `body`s are evaluated if any of the `datum`s match. 19 | 20 | Example: 21 | 22 | ```racket 23 | > (let ([v (random 6)]) 24 | (printf "~a\n" v) 25 | (case v 26 | [(0) 'zero] 27 | [(1) 'one] 28 | [(2) 'two] 29 | [(3 4 5) 'many])) 30 | 0 31 | 'zero 32 | ``` 33 | 34 | The last clause of a `case` form can use `else`, just like `cond`: 35 | 36 | Example: 37 | 38 | ```racket 39 | > (case (random 6) 40 | [(0) 'zero] 41 | [(1) 'one] 42 | [(2) 'two] 43 | [else 'many]) 44 | 'many 45 | ``` 46 | 47 | For more general pattern matching \(but without the dispatch-time 48 | guarantee\), use `match`, which is introduced in \[missing\]. 49 | -------------------------------------------------------------------------------- /qtest/mds/char-strings.md: -------------------------------------------------------------------------------- 1 | # Strings \(Unicode\) 2 | 3 | A _string_ is a fixed-length array of characters. It prints using 4 | doublequotes, where doublequote and backslash characters within the 5 | string are escaped with backslashes. Other common string escapes are 6 | supported, including `\n` for a linefeed, `\r` for a carriage return, 7 | octal escapes using `\` followed by up to three octal digits, and 8 | hexadecimal escapes with `\u` \(up to four digits\). Unprintable 9 | characters in a string are normally shown with `\u` when the string is 10 | printed. 11 | 12 | > +\[missing\] in \[missing\] documents the fine points of the syntax of 13 | > strings. 14 | 15 | The `display` procedure directly writes the characters of a string to 16 | the current output port \(see \[missing\]\), in contrast to the 17 | string-constant syntax used to print a string result. 18 | 19 | Examples: 20 | 21 | ```racket 22 | > "Apple" 23 | "Apple" 24 | > "\u03BB" 25 | "λ" 26 | > (display "Apple") 27 | Apple 28 | > (display "a \"quoted\" thing") 29 | a "quoted" thing 30 | > (display "two\nlines") 31 | two 32 | lines 33 | > (display "\u03BB") 34 | λ 35 | ``` 36 | 37 | A string can be mutable or immutable; strings written directly as 38 | expressions are immutable, but most other strings are mutable. The 39 | `make-string` procedure creates a mutable string given a length and 40 | optional fill character. The `string-ref` procedure accesses a character 41 | from a string \(with 0-based indexing\); the `string-set!` procedure 42 | changes a character in a mutable string. 43 | 44 | Examples: 45 | 46 | ```racket 47 | > (string-ref "Apple" 0) 48 | #\A 49 | > (define s (make-string 5 #\.)) 50 | > s 51 | "....." 52 | > (string-set! s 2 #\λ) 53 | > s 54 | "..λ.." 55 | ``` 56 | 57 | String ordering and case operations are generally _locale-independent_; 58 | that is, they work the same for all users. A few _locale-dependent_ 59 | operations are provided that allow the way that strings are case-folded 60 | and sorted to depend on the end-user’s locale. If you’re sorting 61 | strings, for example, use `string (string (string-ci (string-upcase "Straße") 74 | "STRASSE" 75 | > (parameterize ([current-locale "C"]) 76 | (string-locale-upcase "Straße")) 77 | "STRAßE" 78 | ``` 79 | 80 | For working with plain ASCII, working with raw bytes, or 81 | encoding/decoding Unicode strings as bytes, use byte strings. 82 | 83 | > +\[missing\] in \[missing\] provides more on strings and string 84 | > procedures. 85 | -------------------------------------------------------------------------------- /qtest/mds/chars.md: -------------------------------------------------------------------------------- 1 | # Characters 2 | 3 | A Racket _character_ corresponds to a Unicode _scalar value_. Roughly, a 4 | scalar value is an unsigned integer whose representation fits into 21 5 | bits, and that maps to some notion of a natural-language character or 6 | piece of a character. Technically, a scalar value is a simpler notion 7 | than the concept called a “character” in the Unicode standard, but it’s 8 | an approximation that works well for many purposes. For example, any 9 | accented Roman letter can be represented as a scalar value, as can any 10 | common Chinese character. 11 | 12 | Although each Racket character corresponds to an integer, the character 13 | datatype is separate from numbers. The `char->integer` and 14 | `integer->char` procedures convert between scalar-value numbers and the 15 | corresponding character. 16 | 17 | A printable character normally prints as `#\` followed by the 18 | represented character. An unprintable character normally prints as `#\u` 19 | followed by the scalar value as hexadecimal number. A few characters are 20 | printed specially; for example, the space and linefeed characters print 21 | as `#\space` and `#\newline`, respectively. 22 | 23 | > +\[missing\] in \[missing\] documents the fine points of the syntax of 24 | > characters. 25 | 26 | Examples: 27 | 28 | ```racket 29 | > (integer->char 65) 30 | #\A 31 | > (char->integer #\A) 32 | 65 33 | > #\λ 34 | #\λ 35 | > #\u03BB 36 | #\λ 37 | > (integer->char 17) 38 | #\u0011 39 | > (char->integer #\space) 40 | 32 41 | ``` 42 | 43 | The `display` procedure directly writes a character to the current 44 | output port \(see \[missing\]\), in contrast to the character-constant 45 | syntax used to print a character result. 46 | 47 | Examples: 48 | 49 | ```racket 50 | > #\A 51 | #\A 52 | > (display #\A) 53 | A 54 | ``` 55 | 56 | Racket provides several classification and conversion procedures on 57 | characters. Beware, however, that conversions on some Unicode characters 58 | work as a human would expect only when they are in a string \(e.g., 59 | upcasing “ß” or downcasing “Σ”\). 60 | 61 | Examples: 62 | 63 | ```racket 64 | > (char-alphabetic? #\A) 65 | #t 66 | > (char-numeric? #\0) 67 | #t 68 | > (char-whitespace? #\newline) 69 | #t 70 | > (char-downcase #\A) 71 | #\a 72 | > (char-upcase #\ß) 73 | #\ß 74 | ``` 75 | 76 | The `char=?` procedure compares two or more characters, and `char-ci=?` 77 | compares characters ignoring case. The `eqv?` and `equal?` procedures 78 | behave the same as `char=?` on characters; use `char=?` when you want to 79 | more specifically declare that the values being compared are characters. 80 | 81 | Examples: 82 | 83 | ```racket 84 | > (char=? #\a #\A) 85 | #f 86 | > (char-ci=? #\a #\A) 87 | #t 88 | > (eqv? #\a #\A) 89 | #f 90 | ``` 91 | 92 | > +\[missing\] in \[missing\] provides more on characters and character 93 | > procedures. 94 | -------------------------------------------------------------------------------- /qtest/mds/cmdline.md: -------------------------------------------------------------------------------- 1 | # Command-Line Tools 2 | 3 | Racket provides, as part of its standard distribution, a number of 4 | command-line tools that can make racketeering more pleasant. 5 | 6 | ## 1. Compilation and Configuration: `raco` 7 | 8 | The `raco` \(short for “**Ra**cket **co**mmand”\) program provides a 9 | command-line interface to many additional tools for compiling Racket 10 | programs and maintaining a Racket installation. 11 | 12 | * `raco make` compiles Racket source to bytecode. 13 | 14 | For example, if you have a program `"take-over-world.rkt"` and you’d 15 | like to compile it to bytecode, along with all of its dependencies, so 16 | that it loads more quickly, then run 17 | 18 |   `raco make take-over-the-world.rkt` 19 | 20 | The bytecode file is written as `"take-over-the-world_rkt.zo"` in a 21 | `"compiled"` subdirectory; `".zo"` is the file suffix for a bytecode 22 | file. 23 | 24 | * `raco setup` manages a Racket installation, including manually 25 | installed packages. 26 | 27 | For example, if you create your own library collection called 28 | `"take-over"`, and you’d like to build all bytecode and documentation 29 | for the collection, then run 30 | 31 |   `raco setup take-over` 32 | 33 | * `raco pkg` manages packages that can be installed through the Racket 34 | package manager. 35 | 36 | For example, to see the list of installed packages run: 37 | 38 |   `raco pkg show` 39 | 40 | To install a new package named `` run: 41 | 42 |   `raco pkg install ` 43 | 44 | See \[missing\] for more details about package management. 45 | 46 | For more information on `raco`, see \[missing\]. 47 | 48 | ## 2. Interactive evaluation 49 | 50 | The Racket REPL provides everything you expect from a modern interactive 51 | environment. For example, it provides an `,enter` command to have a REPL 52 | that runs in the context of a given module, and an `,edit` command to 53 | invoke your editor \(as specified by the `EDITOR` environment variable\) 54 | on the file you entered. A `,drracket` command makes it easy to use your 55 | favorite editor to write code, and still have DrRacket at hand to try 56 | things out. 57 | 58 | For more information, see \[missing\]. 59 | 60 | ## 3. Shell completion 61 | 62 | Shell auto-completion for `bash` and `zsh` is available in 63 | `"share/pkgs/shell-completion/racket-completion.bash"` and 64 | `"share/pkgs/shell-completion/racket-completion.zsh"`, respectively. To 65 | enable it, just run the appropriate file from your `.bashrc` or your 66 | `.zshrc`. 67 | 68 | The `"shell-completion"` collection is only available in the Racket Full 69 | distribution. The completion scripts are also available 70 | [online](https://github.com/racket/shell-completion). 71 | -------------------------------------------------------------------------------- /qtest/mds/compile.md: -------------------------------------------------------------------------------- 1 | # Compilation and Configuration: `raco` 2 | 3 | The `raco` \(short for “**Ra**cket **co**mmand”\) program provides a 4 | command-line interface to many additional tools for compiling Racket 5 | programs and maintaining a Racket installation. 6 | 7 | * `raco make` compiles Racket source to bytecode. 8 | 9 | For example, if you have a program `"take-over-world.rkt"` and you’d 10 | like to compile it to bytecode, along with all of its dependencies, so 11 | that it loads more quickly, then run 12 | 13 |   `raco make take-over-the-world.rkt` 14 | 15 | The bytecode file is written as `"take-over-the-world_rkt.zo"` in a 16 | `"compiled"` subdirectory; `".zo"` is the file suffix for a bytecode 17 | file. 18 | 19 | * `raco setup` manages a Racket installation, including manually 20 | installed packages. 21 | 22 | For example, if you create your own library collection called 23 | `"take-over"`, and you’d like to build all bytecode and documentation 24 | for the collection, then run 25 | 26 |   `raco setup take-over` 27 | 28 | * `raco pkg` manages packages that can be installed through the Racket 29 | package manager. 30 | 31 | For example, to see the list of installed packages run: 32 | 33 |   `raco pkg show` 34 | 35 | To install a new package named `` run: 36 | 37 |   `raco pkg install ` 38 | 39 | See \[missing\] for more details about package management. 40 | 41 | For more information on `raco`, see \[missing\]. 42 | -------------------------------------------------------------------------------- /qtest/mds/dialects.md: -------------------------------------------------------------------------------- 1 | # Dialects of Racket and Scheme 2 | 3 | We use “Racket” to refer to a specific dialect of the Lisp language, and 4 | one that is based on the Scheme branch of the Lisp family. Despite 5 | Racket’s similarity to Scheme, the `#lang` prefix on modules is a 6 | particular feature of Racket, and programs that start with `#lang` are 7 | unlikely to run in other implementations of Scheme. At the same time, 8 | programs that do not start with `#lang` do not work with the default 9 | mode of most Racket tools. 10 | 11 | “Racket” is not, however, the only dialect of Lisp that is supported by 12 | Racket tools. On the contrary, Racket tools are designed to support 13 | multiple dialects of Lisp and even multiple languages, which allows the 14 | Racket tool suite to serve multiple communities. Racket also gives 15 | programmers and researchers the tools they need to explore and create 16 | new languages. 17 | 18 | 1 More Rackets 19 | 20 | 2 Standards 21 | 2.1 R5RS 22 | 2.2 R6RS 23 | 24 | 3 Teaching 25 | 26 | ## 1. More Rackets 27 | 28 | “Racket” is more of an idea about programming languages than a language 29 | in the usual sense. Macros can extend a base language \(as described in 30 | \[missing\]\), and alternate parsers can construct an entirely new 31 | language from the ground up \(as described in \[missing\]\). 32 | 33 | The `#lang` line that starts a Racket module declares the base language 34 | of the module. By “Racket,” we usually mean `#lang` followed by the base 35 | language `racket` or `racket/base` \(of which `racket` is an 36 | extension\). The Racket distribution provides additional languages, 37 | including the following: 38 | 39 | * `typed/racket` — like `racket`, but statically typed; see \[missing\] 40 | 41 | * `lazy` — like `racket/base`, but avoids evaluating an expression until 42 | its value is needed; see the Lazy Racket documentation. 43 | 44 | * `frtime` — changes evaluation in an even more radical way to support 45 | reactive programming; see the FrTime documentation. 46 | 47 | * `scribble/base` — a language, which looks more like Latex than Racket, 48 | for writing documentation; see \[missing\] 49 | 50 | Each of these languages is used by starting module with the language 51 | name after `#lang`. For example, this source of this document starts 52 | with `#lang scribble/base`. 53 | 54 | Furthermore, Racket users can define their own languages, as discussed 55 | in \[missing\]. Typically, a language name maps to its implementation 56 | through a module path by adding `/lang/reader`; for example, the 57 | language name `scribble/base` is expanded to 58 | `scribble/base/lang/reader`, which is the module that implements the 59 | surface-syntax parser. Some language names act as language loaders; for 60 | example, `#lang planet planet-path` downloads, installs, and uses a 61 | language via PLaneT. 62 | 63 | ## 2. Standards 64 | 65 | Standard dialects of Scheme include the ones defined by R5RS and R6RS. 66 | 67 | ### 2.1. R5RS 68 | 69 | “R5RS” stands for [The Revised5 Report on the Algorithmic Language 70 | Scheme](../r5rs/r5rs-std/index.html), and it is currently the most 71 | widely implemented Scheme standard. 72 | 73 | Racket tools in their default modes do not conform to R5RS, mainly 74 | because Racket tools generally expect modules, and R5RS does not define 75 | a module system. Typical single-file R5RS programs can be converted to 76 | Racket programs by prefixing them with `#lang r5rs`, but other Scheme 77 | systems do not recognize `#lang r5rs`. The `plt-r5rs` executable \(see 78 | \[missing\]\) more directly conforms to the R5RS standard. 79 | 80 | Aside from the module system, the syntactic forms and functions of R5RS 81 | and Racket differ. Only simple R5RS become Racket programs when prefixed 82 | with `#lang racket`, and relatively few Racket programs become R5RS 83 | programs when a `#lang` line is removed. Also, when mixing “R5RS 84 | modules” with Racket modules, beware that R5RS pairs correspond to 85 | Racket mutable pairs \(as constructed with `mcons`\). 86 | 87 | See \[missing\] for more information about running R5RS programs with 88 | Racket. 89 | 90 | ### 2.2. R6RS 91 | 92 | “R6RS” stands for [The Revised6 Report on the Algorithmic Language 93 | Scheme](../r6rs/r6rs-std/index.html), which extends R5RS with a module 94 | system that is similar to the Racket module system. 95 | 96 | When an R6RS library or top-level program is prefixed with `#!r6rs` 97 | \(which is valid R6RS syntax\), then it can also be used as a Racket 98 | program. This works because `#!` in Racket is treated as a shorthand for 99 | `#lang` followed by a space, so `#!r6rs` selects the `r6rs` module 100 | language. As with R5RS, however, beware that the syntactic forms and 101 | functions of R6RS differ from Racket, and R6RS pairs are mutable pairs. 102 | 103 | See \[missing\] for more information about running R6RS programs with 104 | Racket. 105 | 106 | ## 3. Teaching 107 | 108 | The _[How to Design Programs](http://www.htdp.org)_ textbook relies on 109 | pedagogic variants of Racket that smooth the introduction of programming 110 | concepts for new programmers. See the _[How to Design 111 | Programs](http://www.htdp.org)_ language documentation. 112 | 113 | The _[How to Design Programs](http://www.htdp.org)_ languages are 114 | typically not used with `#lang` prefixes, but are instead used within 115 | DrRacket by selecting the language from the Choose Language... dialog. 116 | -------------------------------------------------------------------------------- /qtest/mds/graphics.md: -------------------------------------------------------------------------------- 1 | # Graphics and GUIs 2 | 3 | Racket provides many libraries for graphics and graphical user 4 | interfaces \(GUIs\): 5 | 6 | * The `racket/draw` library provides basic drawing tools, including 7 | drawing contexts such as bitmaps and PostScript files. 8 | 9 | See \[missing\] for more information. 10 | 11 | * The `racket/gui` library provides GUI widgets such as windows, 12 | buttons, checkboxes, and text fields. The library also includes a 13 | sophisticated and extensible text editor. 14 | 15 | See \[missing\] for more information. 16 | 17 | * The `pict` library provides a more functional abstraction layer over 18 | `racket/draw`. This layer is especially useful for creating slide 19 | presentations with Slideshow, but it is also useful for creating 20 | images for Scribble documents or other drawing tasks. Pictures created 21 | with the `pict` library can be rendered to any drawing context. 22 | 23 | See \[missing\] for more information. 24 | 25 | * The `2htdp/image` library is similar to `pict`. It is more streamlined 26 | for pedagogical use, but also slightly more specific to screen and 27 | bitmap drawing. 28 | 29 | See `2htdp/image` for more information. 30 | 31 | * The `sgl` library provides OpenGL for 3-D graphics. The context for 32 | rendering OpenGL can be a window or bitmap created with `racket/gui`. 33 | 34 | See the SGL documentation for more information. 35 | -------------------------------------------------------------------------------- /qtest/mds/hash-tables.md: -------------------------------------------------------------------------------- 1 | # Hash Tables 2 | 3 | A _hash table_ implements a mapping from keys to values, where both keys 4 | and values can be arbitrary Racket values, and access and update to the 5 | table are normally constant-time operations. Keys are compared using 6 | `equal?`, `eqv?`, or `eq?`, depending on whether the hash table is 7 | created with `make-hash`, `make-hasheqv`, or `make-hasheq`. 8 | 9 | Examples: 10 | 11 | ```racket 12 | > (define ht (make-hash)) 13 | > (hash-set! ht "apple" '(red round)) 14 | > (hash-set! ht "banana" '(yellow long)) 15 | > (hash-ref ht "apple") 16 | '(red round) 17 | > (hash-ref ht "coconut") 18 | hash-ref: no value found for key 19 | key: "coconut" 20 | > (hash-ref ht "coconut" "not there") 21 | "not there" 22 | ``` 23 | 24 | The `hash`, `hasheqv`, and `hasheq` functions create immutable hash 25 | tables from an initial set of keys and values, in which each value is 26 | provided as an argument after its key. Immutable hash tables can be 27 | extended with `hash-set`, which produces a new immutable hash table in 28 | constant time. 29 | 30 | Examples: 31 | 32 | ```racket 33 | > (define ht (hash "apple" 'red "banana" 'yellow)) 34 | > (hash-ref ht "apple") 35 | 'red 36 | > (define ht2 (hash-set ht "coconut" 'brown)) 37 | > (hash-ref ht "coconut") 38 | hash-ref: no value found for key 39 | key: "coconut" 40 | > (hash-ref ht2 "coconut") 41 | 'brown 42 | ``` 43 | 44 | A literal immutable hash table can be written as an expression by using 45 | `#hash` \(for an `equal?`-based table\), `#hasheqv` \(for an 46 | `eqv?`-based table\), or `#hasheq` \(for an `eq?`-based table\). A 47 | parenthesized sequence must immediately follow `#hash`, `#hasheq`, or 48 | `#hasheqv`, where each element is a dotted key–value pair. The `#hash`, 49 | etc. forms implicitly `quote` their key and value sub-forms. 50 | 51 | Examples: 52 | 53 | ```racket 54 | > (define ht #hash(("apple" . red) 55 | ("banana" . yellow))) 56 | > (hash-ref ht "apple") 57 | 'red 58 | ``` 59 | 60 | > +\[missing\] in \[missing\] documents the fine points of the syntax of 61 | > hash table literals. 62 | 63 | Both mutable and immutable hash tables print like immutable hash tables, 64 | using a quoted `#hash`, `#hasheqv`, or `#hasheq` form if all keys and 65 | values can be expressed with `quote` or using `hash`, `hasheq`, or 66 | `hasheqv` otherwise: 67 | 68 | Examples: 69 | 70 | ```racket 71 | > #hash(("apple" . red) 72 | ("banana" . yellow)) 73 | '#hash(("banana" . yellow) ("apple" . red)) 74 | > (hash 1 (srcloc "file.rkt" 1 0 1 (+ 4 4))) 75 | (hash 1 (srcloc "file.rkt" 1 0 1 8)) 76 | ``` 77 | 78 | A mutable hash table can optionally retain its keys _weakly_, so each 79 | mapping is retained only so long as the key is retained elsewhere. 80 | 81 | Examples: 82 | 83 | ```racket 84 | > (define ht (make-weak-hasheq)) 85 | > (hash-set! ht (gensym) "can you see me?") 86 | > (collect-garbage) 87 | > (hash-count ht) 88 | 0 89 | ``` 90 | 91 | Beware that even a weak hash table retains its values strongly, as long 92 | as the corresponding key is accessible. This creates a catch-22 93 | dependency when a value refers back to its key, so that the mapping is 94 | retained permanently. To break the cycle, map the key to an _ephemeron_ 95 | that pairs the value with its key \(in addition to the implicit pairing 96 | of the hash table\). 97 | 98 | > +\[missing\] in \[missing\] documents the fine points of using 99 | > ephemerons. 100 | 101 | Examples: 102 | 103 | ```racket 104 | > (define ht (make-weak-hasheq)) 105 | > (let ([g (gensym)]) 106 | (hash-set! ht g (list g))) 107 | > (collect-garbage) 108 | > (hash-count ht) 109 | 1 110 | ``` 111 | 112 | ```racket 113 | > (define ht (make-weak-hasheq)) 114 | > (let ([g (gensym)]) 115 | (hash-set! ht g (make-ephemeron g (list g)))) 116 | > (collect-garbage) 117 | > (hash-count ht) 118 | 0 119 | ``` 120 | 121 | > +\[missing\] in \[missing\] provides more on hash tables and hash-table 122 | > procedures. 123 | -------------------------------------------------------------------------------- /qtest/mds/keywords.md: -------------------------------------------------------------------------------- 1 | # Keywords 2 | 3 | A _keyword_ value is similar to a symbol \(see \[missing\]\), but its 4 | printed form is prefixed with `#:`. 5 | 6 | > +\[missing\] in \[missing\] documents the fine points of the syntax of 7 | > keywords. 8 | 9 | Examples: 10 | 11 | ```racket 12 | > (string->keyword "apple") 13 | '#:apple 14 | > '#:apple 15 | '#:apple 16 | > (eq? '#:apple (string->keyword "apple")) 17 | #t 18 | ``` 19 | 20 | More precisely, a keyword is analogous to an identifier; in the same way 21 | that an identifier can be quoted to produce a symbol, a keyword can be 22 | quoted to produce a value. The same term “keyword” is used in both 23 | cases, but we sometimes use _keyword value_ to refer more specifically 24 | to the result of a quote-keyword expression or of `string->keyword`. An 25 | unquoted keyword is not an expression, just as an unquoted identifier 26 | does not produce a symbol: 27 | 28 | Examples: 29 | 30 | ```racket 31 | > not-a-symbol-expression 32 | not-a-symbol-expression: undefined; 33 | cannot reference an identifier before its definition 34 | in module: top-level 35 | > #:not-a-keyword-expression 36 | eval:2:0: #%datum: keyword misused as an expression 37 | at: #:not-a-keyword-expression 38 | ``` 39 | 40 | Despite their similarities, keywords are used in a different way than 41 | identifiers or symbols. Keywords are intended for use \(unquoted\) as 42 | special markers in argument lists and in certain syntactic forms. For 43 | run-time flags and enumerations, use symbols instead of keywords. The 44 | example below illustrates the distinct roles of keywords and symbols. 45 | 46 | Examples: 47 | 48 | ```racket 49 | > (define dir (find-system-path 'temp-dir)) ; not '#:temp-dir 50 | > (with-output-to-file (build-path dir "stuff.txt") 51 | (lambda () (printf "example\n")) 52 | ; optional #:mode argument can be 'text or 'binary 53 | #:mode 'text 54 | ; optional #:exists argument can be 'replace, 'truncate, ... 55 | #:exists 'replace) 56 | ``` 57 | -------------------------------------------------------------------------------- /qtest/mds/match.md: -------------------------------------------------------------------------------- 1 | # Pattern Matching 2 | 3 | The `match` form supports pattern matching on arbitrary Racket values, 4 | as opposed to functions like `regexp-match` that compare regular 5 | expressions to byte and character sequences \(see \[missing\]\). 6 | 7 | ```racket 8 | (match target-expr 9 | [pattern expr ...+] ...) 10 | ``` 11 | 12 | The `match` form takes the result of `target-expr` and tries to match 13 | each `pattern` in order. As soon as it finds a match, it evaluates the 14 | corresponding `expr` sequence to obtain the result for the `match` form. 15 | If `pattern` includes _pattern variables_, they are treated like 16 | wildcards, and each variable is bound in the `expr` to the input 17 | fragments that it matched. 18 | 19 | Most Racket literal expressions can be used as patterns: 20 | 21 | ```racket 22 | > (match 2 23 | [1 'one] 24 | [2 'two] 25 | [3 'three]) 26 | 'two 27 | > (match #f 28 | [#t 'yes] 29 | [#f 'no]) 30 | 'no 31 | > (match "apple" 32 | ['apple 'symbol] 33 | ["apple" 'string] 34 | [#f 'boolean]) 35 | 'string 36 | ``` 37 | 38 | Constructors like `cons`, `list`, and `vector` can be used to create 39 | patterns that match pairs, lists, and vectors: 40 | 41 | ```racket 42 | > (match '(1 2) 43 | [(list 0 1) 'one] 44 | [(list 1 2) 'two]) 45 | 'two 46 | > (match '(1 . 2) 47 | [(list 1 2) 'list] 48 | [(cons 1 2) 'pair]) 49 | 'pair 50 | > (match #(1 2) 51 | [(list 1 2) 'list] 52 | [(vector 1 2) 'vector]) 53 | 'vector 54 | ``` 55 | 56 | A constructor bound with `struct` also can be used as a pattern 57 | constructor: 58 | 59 | ```racket 60 | > (struct shoe (size color)) 61 | > (struct hat (size style)) 62 | > (match (hat 23 'bowler) 63 | [(shoe 10 'white) "bottom"] 64 | [(hat 23 'bowler) "top"]) 65 | "top" 66 | ``` 67 | 68 | Unquoted, non-constructor identifiers in a pattern are pattern variables 69 | that are bound in the result expressions, except `_`, which does not 70 | bind \(and thus is usually used as a catch-all\): 71 | 72 | ```racket 73 | > (match '(1) 74 | [(list x) (+ x 1)] 75 | [(list x y) (+ x y)]) 76 | 2 77 | > (match '(1 2) 78 | [(list x) (+ x 1)] 79 | [(list x y) (+ x y)]) 80 | 3 81 | > (match (hat 23 'bowler) 82 | [(shoe sz col) sz] 83 | [(hat sz stl) sz]) 84 | 23 85 | > (match (hat 11 'cowboy) 86 | [(shoe sz 'black) 'a-good-shoe] 87 | [(hat sz 'bowler) 'a-good-hat] 88 | [_ 'something-else]) 89 | 'something-else 90 | ``` 91 | 92 | An ellipsis, written `...`, acts like a Kleene star within a list or 93 | vector pattern: the preceding sub-pattern can be used to match any 94 | number of times for any number of consecutive elements of the list or 95 | vector. If a sub-pattern followed by an ellipsis includes a pattern 96 | variable, the variable matches multiple times, and it is bound in the 97 | result expression to a list of matches: 98 | 99 | ```racket 100 | > (match '(1 1 1) 101 | [(list 1 ...) 'ones] 102 | [_ 'other]) 103 | 'ones 104 | > (match '(1 1 2) 105 | [(list 1 ...) 'ones] 106 | [_ 'other]) 107 | 'other 108 | > (match '(1 2 3 4) 109 | [(list 1 x ... 4) x]) 110 | '(2 3) 111 | > (match (list (hat 23 'bowler) (hat 22 'pork-pie)) 112 | [(list (hat sz styl) ...) (apply + sz)]) 113 | 45 114 | ``` 115 | 116 | Ellipses can be nested to match nested repetitions, and in that case, 117 | pattern variables can be bound to lists of lists of matches: 118 | 119 | ```racket 120 | > (match '((! 1) (! 2 2) (! 3 3 3)) 121 | [(list (list '! x ...) ...) x]) 122 | '((1) (2 2) (3 3 3)) 123 | ``` 124 | 125 | The `quasiquote` form \(see \[missing\] for more about it\) can also be 126 | used to build patterns. While unquoted portions of a normal quasiquoted 127 | form mean regular racket evaluation, here unquoted portions mean go back 128 | to regular pattern matching. 129 | 130 | So, in the example below, the with expression is the pattern and it gets 131 | rewritten into the application expression, using quasiquote as a pattern 132 | in the first instance and quasiquote to build an expression in the 133 | second. 134 | 135 | ```racket 136 | > (match `{with {x 1} {+ x 1}} 137 | [`{with {,id ,rhs} ,body} 138 | `{{lambda {,id} ,body} ,rhs}]) 139 | '((lambda (x) (+ x 1)) 1) 140 | ``` 141 | 142 | For information on many more pattern forms, see `racket/match`. 143 | 144 | Forms like `match-let` and `match-lambda` support patterns in positions 145 | that otherwise must be identifiers. For example, `match-let` generalizes 146 | `let` to a destructing bind: 147 | 148 | ```racket 149 | > (match-let ([(list x y z) '(1 2 3)]) 150 | (list z y x)) 151 | '(3 2 1) 152 | ``` 153 | 154 | For information on these additional forms, see `racket/match`. 155 | 156 | > +\[missing\] in \[missing\] provides more on pattern matching. 157 | -------------------------------------------------------------------------------- /qtest/mds/module-macro.md: -------------------------------------------------------------------------------- 1 | # Modules and Macros 2 | 3 | Racket’s module system cooperates closely with Racket’s macro system for 4 | adding new syntactic forms to Racket. For example, in the same way that 5 | importing `racket/base` introduces syntax for `require` and `lambda`, 6 | importing other modules can introduce new syntactic forms \(in addition 7 | to more traditional kinds of imports, such as functions or constants\). 8 | 9 | We introduce macros in more detail later, in \[missing\], but here’s a 10 | simple example of a module that defines a pattern-based macro: 11 | 12 | ```racket 13 | (module noisy racket 14 | (provide define-noisy) 15 | 16 | (define-syntax-rule (define-noisy (id arg ...) body) 17 | (define (id arg ...) 18 | (show-arguments 'id (list arg ...)) 19 | body)) 20 | 21 | (define (show-arguments name args) 22 | (printf "calling ~s with arguments ~e" name args))) 23 | ``` 24 | 25 | The `define-noisy` binding provided by this module is a macro that acts 26 | like `define` for a function, but it causes each call to the function to 27 | print the arguments that are provided to the function: 28 | 29 | ```racket 30 | > (require 'noisy) 31 | > (define-noisy (f x y) 32 | (+ x y)) 33 | > (f 1 2) 34 | calling f with arguments '(1 2) 35 | 3 36 | ``` 37 | 38 | Roughly, the `define-noisy` form works by replacing 39 | 40 | ```racket 41 | (define-noisy (f x y) 42 | (+ x y)) 43 | ``` 44 | 45 | with 46 | 47 | ```racket 48 | (define (f x y) 49 | (show-arguments 'f (list x y)) 50 | (+ x y)) 51 | ``` 52 | 53 | Since `show-arguments` isn’t provided by the `noisy` module, however, 54 | this literal textual replacement is not quite right. The actual 55 | replacement correctly tracks the origin of identifiers like 56 | `show-arguments`, so they can refer to other definitions in the place 57 | where the macro is defined—even if those identifiers are not available 58 | at the place where the macro is used. 59 | 60 | There’s more to the macro and module interaction than identifier 61 | binding. The `define-syntax-rule` form is itself a macro, and it expands 62 | to compile-time code that implements the transformation from 63 | `define-noisy` into `define`. The module system keeps track of which 64 | code needs to run at compile and which needs to run normally, as 65 | explained more in \[missing\] and \[missing\]. 66 | -------------------------------------------------------------------------------- /qtest/mds/module-provide.md: -------------------------------------------------------------------------------- 1 | # Exports: `provide` 2 | 3 | By default, all of a module’s definitions are private to the module. The 4 | `provide` form specifies definitions to be made available where the 5 | module is `require`d. 6 | 7 | ```racket 8 | (provide provide-spec ...) 9 | ``` 10 | 11 | A `provide` form can only appear at module level \(i.e., in the 12 | immediate body of a `module`\). Specifying multiple `provide-spec`s in 13 | a single `provide` is exactly the same as using multiple `provide`s each 14 | with a single `provide-spec`. 15 | 16 | Each identifier can be exported at most once from a module across all 17 | `provide`s within the module. More precisely, the external name for each 18 | export must be distinct; the same internal binding can be exported 19 | multiple times with different external names. 20 | 21 | The allowed shape of a `provide-spec` is defined recursively: 22 | 23 | ```racket 24 | identifier 25 | ``` 26 | In its simplest form, a `provide-spec` indicates a binding within its 27 | module to be exported. The binding can be from either a local 28 | definition, or from an import. 29 | 30 | ```racket 31 | (rename-out [orig-id export-id] ...) 32 | ``` 33 | A `rename-out` form is similar to just specifying an identifier, but the 34 | exported binding `orig-id` is given a different name, `export-id`, to 35 | importing modules. 36 | 37 | ```racket 38 | (struct-out struct-id) 39 | ``` 40 | A `struct-out` form exports the bindings created by `(struct struct-id 41 | ....)`. 42 | > +See \[missing\] for information on `define-struct`. 43 | 44 | ```racket 45 | (all-defined-out) 46 | ``` 47 | The `all-defined-out` shorthand exports all bindings that are defined 48 | within the exporting module \(as opposed to imported\). 49 | Use of the `all-defined-out` shorthand is generally discouraged, because 50 | it makes less clear the actual exports for a module, and because Racket 51 | programmers get into the habit of thinking that definitions can be added 52 | freely to a module without affecting its public interface \(which is not 53 | the case when `all-defined-out` is used\). 54 | 55 | ```racket 56 | (all-from-out module-path) 57 | ``` 58 | The `all-from-out` shorthand exports all bindings in the module that 59 | were imported using a `require-spec` that is based on `module-path`. 60 | Although different `module-path`s could refer to the same file-based 61 | module, re-exporting with `all-from-out` is based specifically on the 62 | `module-path` reference, and not the module that is actually referenced. 63 | 64 | ```racket 65 | (except-out provide-spec id ...) 66 | ``` 67 | Like `provide-spec`, but omitting the export of each `id`, where `id` is 68 | the external name of the binding to omit. 69 | 70 | ```racket 71 | (prefix-out prefix-id provide-spec) 72 | ``` 73 | Like `provide-spec`, but adding `prefix-id` to the beginning of the 74 | external name for each exported binding. 75 | -------------------------------------------------------------------------------- /qtest/mds/module-require.md: -------------------------------------------------------------------------------- 1 | # Imports: `require` 2 | 3 | The `require` form imports from another module. A `require` form can 4 | appear within a module, in which case it introduces bindings from the 5 | specified module into importing module. A `require` form can also appear 6 | at the top level, in which case it both imports bindings and 7 | _instantiates_ the specified module; that is, it evaluates the body 8 | definitions and expressions of the specified module, if they have not 9 | been evaluated already. 10 | 11 | A single `require` can specify multiple imports at once: 12 | 13 | ```racket 14 | (require require-spec ...) 15 | ``` 16 | 17 | Specifying multiple `require-spec`s in a single `require` is essentially 18 | the same as using multiple `require`s, each with a single 19 | `require-spec`. The difference is minor, and confined to the top-level: 20 | a single `require` can import a given identifier at most once, whereas a 21 | separate `require` can replace the bindings of a previous `require` 22 | \(both only at the top level, outside of a module\). 23 | 24 | The allowed shape of a `require-spec` is defined recursively: 25 | 26 | ```racket 27 | module-path 28 | ``` 29 | In its simplest form, a `require-spec` is a `module-path` \(as defined 30 | in the previous section, \[missing\]\). In this case, the bindings 31 | introduced by `require` are determined by `provide` declarations within 32 | each module referenced by each `module-path`. 33 | Examples: 34 | 35 | ```racket 36 | > (module m racket 37 | (provide color) 38 | (define color "blue")) 39 | > (module n racket 40 | (provide size) 41 | (define size 17)) 42 | > (require 'm 'n) 43 | > (list color size) 44 | '("blue" 17) 45 | ``` 46 | 47 | ```racket 48 | (only-in require-spec id-maybe-renamed ...) 49 | 50 | id-maybe-renamed = id 51 | | [orig-id bind-id] 52 | ``` 53 | An `only-in` form limits the set of bindings that would be introduced by 54 | a base `require-spec`. Also, `only-in` optionally renames each binding 55 | that is preserved: in a `[orig-id bind-id]` form, the `orig-id` refers 56 | to a binding implied by `require-spec`, and `bind-id` is the name that 57 | will be bound in the importing context instead of `orig-id`. 58 | Examples: 59 | 60 | ```racket 61 | > (module m (lib "racket") 62 | (provide tastes-great? 63 | less-filling?) 64 | (define tastes-great? #t) 65 | (define less-filling? #t)) 66 | > (require (only-in 'm tastes-great?)) 67 | > tastes-great? 68 | #t 69 | > less-filling? 70 | less-filling?: undefined; 71 | cannot reference an identifier before its definition 72 | in module: top-level 73 | > (require (only-in 'm [less-filling? lite?])) 74 | > lite? 75 | #t 76 | ``` 77 | 78 | ```racket 79 | (except-in require-spec id ...) 80 | ``` 81 | This form is the complement of `only-in`: it excludes specific bindings 82 | from the set specified by `require-spec`. 83 | 84 | ```racket 85 | (rename-in require-spec [orig-id bind-id] ...) 86 | ``` 87 | This form supports renaming like `only-in`, but leaving alone 88 | identifiers from `require-spec` that are not mentioned as an `orig-id`. 89 | 90 | ```racket 91 | (prefix-in prefix-id require-spec) 92 | ``` 93 | This is a shorthand for renaming, where `prefix-id` is added to the 94 | front of each identifier specified by `require-spec`. 95 | 96 | The `only-in`, `except-in`, `rename-in`, and `prefix-in` forms can be 97 | nested to implement more complex manipulations of imported bindings. For 98 | example, 99 | 100 | `(require` `(prefix-in` `m:` `(except-in` `'m` `ghost)))` 101 | 102 | imports all bindings that `m` exports, except for the `ghost` binding, 103 | and with local names that are prefixed with `m:`. 104 | 105 | Equivalently, the `prefix-in` could be applied before `except-in`, as 106 | long as the omission with `except-in` is specified using the `m:` 107 | prefix: 108 | 109 | `(require` `(except-in` `(prefix-in` `m:` `'m)` `m:ghost))` 110 | -------------------------------------------------------------------------------- /qtest/mds/module-set.md: -------------------------------------------------------------------------------- 1 | # Assignment and Redefinition 2 | 3 | The use of `set!` on variables defined within a module is limited to the 4 | body of the defining module. That is, a module is allowed to change the 5 | value of its own definitions, and such changes are visible to importing 6 | modules. However, an importing context is not allowed to change the 7 | value of an imported binding. 8 | 9 | Examples: 10 | 11 | ```racket 12 | > (module m racket 13 | (provide counter increment!) 14 | (define counter 0) 15 | (define (increment!) 16 | (set! counter (add1 counter)))) 17 | > (require 'm) 18 | > counter 19 | 0 20 | > (increment!) 21 | > counter 22 | 1 23 | > (set! counter -1) 24 | set!: cannot mutate module-required identifier 25 | at: counter 26 | in: (set! counter -1) 27 | ``` 28 | 29 | As the above example illustrates, a module can always grant others the 30 | ability to change its exports by providing a mutator function, such as 31 | `increment!`. 32 | 33 | The prohibition on assignment of imported variables helps support 34 | modular reasoning about programs. For example, in the module, 35 | 36 | ```racket 37 | (module m racket 38 | (provide rx:fish fishy-string?) 39 | (define rx:fish #rx"fish") 40 | (define (fishy-string? s) 41 | (regexp-match? rx:fish s))) 42 | ``` 43 | 44 | the function `fishy-string?` will always match strings that contain 45 | “fish”, no matter how other modules use the `rx:fish` binding. For 46 | essentially the same reason that it helps programmers, the prohibition 47 | on assignment to imports also allows many programs to be executed more 48 | efficiently. 49 | 50 | Along the same lines, when a module contains no `set!` of a particular 51 | identifier that is defined within the module, then the identifier is 52 | considered a _constant_ that cannot be changed—not even by re-declaring 53 | the module. 54 | 55 | Consequently, re-declaration of a module is not generally allowed. For 56 | file-based modules, simply changing the file does not lead to a 57 | re-declaration in any case, because file-based modules are loaded on 58 | demand, and the previously loaded declarations satisfy future requests. 59 | It is possible to use Racket’s reflection support to re-declare a 60 | module, however, and non-file modules can be re-declared in the REPL; in 61 | such cases, the re-declaration may fail if it involves the re-definition 62 | of a previously constant binding. 63 | 64 | ```racket 65 | > (module m racket 66 | (define pie 3.141597)) 67 | > (require 'm) 68 | > (module m racket 69 | (define pie 3)) 70 | define-values: assignment disallowed; 71 | cannot re-define a constant 72 | constant: pie 73 | in module: 'm 74 | ``` 75 | 76 | For exploration and debugging purposes, the Racket reflective layer 77 | provides a `compile-enforce-module-constants` parameter to disable the 78 | enforcement of constants. 79 | 80 | ```racket 81 | > (compile-enforce-module-constants #f) 82 | > (module m2 racket 83 | (provide pie) 84 | (define pie 3.141597)) 85 | > (require 'm2) 86 | > (module m2 racket 87 | (provide pie) 88 | (define pie 3)) 89 | > (compile-enforce-module-constants #t) 90 | > pie 91 | 3 92 | ``` 93 | -------------------------------------------------------------------------------- /qtest/mds/named-let.md: -------------------------------------------------------------------------------- 1 | # Named `let` 2 | 3 | A named `let` is an iteration and recursion form. It uses the same 4 | syntactic keyword `let` as for local binding, but an identifier after 5 | the `let` \(instead of an immediate open parenthesis\) triggers a 6 | different parsing. 7 | 8 | ```racket 9 | (let proc-id ([arg-id init-expr] ...) 10 | body ...+) 11 | ``` 12 | 13 | A named `let` form is equivalent to 14 | 15 | ```racket 16 | (letrec ([proc-id (lambda (arg-id ...) 17 | body ...+)]) 18 | (proc-id init-expr ...)) 19 | ``` 20 | 21 | That is, a named `let` binds a function identifier that is visible only 22 | in the function’s body, and it implicitly calls the function with the 23 | values of some initial expressions. 24 | 25 | Examples: 26 | 27 | ```racket 28 | (define (duplicate pos lst) 29 | (let dup ([i 0] 30 | [lst lst]) 31 | (cond 32 | [(= i pos) (cons (car lst) lst)] 33 | [else (cons (car lst) (dup (+ i 1) (cdr lst)))]))) 34 | 35 | > (duplicate 1 (list "apple" "cheese burger!" "banana")) 36 | '("apple" "cheese burger!" "cheese burger!" "banana") 37 | ``` 38 | -------------------------------------------------------------------------------- /qtest/mds/other.md: -------------------------------------------------------------------------------- 1 | # More Libraries 2 | 3 | This guide covers only the Racket language and libraries that are 4 | documented in \[missing\]. The Racket distribution includes many 5 | additional libraries. 6 | 7 | ## 1. Graphics and GUIs 8 | 9 | Racket provides many libraries for graphics and graphical user 10 | interfaces \(GUIs\): 11 | 12 | * The `racket/draw` library provides basic drawing tools, including 13 | drawing contexts such as bitmaps and PostScript files. 14 | 15 | See \[missing\] for more information. 16 | 17 | * The `racket/gui` library provides GUI widgets such as windows, 18 | buttons, checkboxes, and text fields. The library also includes a 19 | sophisticated and extensible text editor. 20 | 21 | See \[missing\] for more information. 22 | 23 | * The `pict` library provides a more functional abstraction layer over 24 | `racket/draw`. This layer is especially useful for creating slide 25 | presentations with Slideshow, but it is also useful for creating 26 | images for Scribble documents or other drawing tasks. Pictures created 27 | with the `pict` library can be rendered to any drawing context. 28 | 29 | See \[missing\] for more information. 30 | 31 | * The `2htdp/image` library is similar to `pict`. It is more streamlined 32 | for pedagogical use, but also slightly more specific to screen and 33 | bitmap drawing. 34 | 35 | See `2htdp/image` for more information. 36 | 37 | * The `sgl` library provides OpenGL for 3-D graphics. The context for 38 | rendering OpenGL can be a window or bitmap created with `racket/gui`. 39 | 40 | See the SGL documentation for more information. 41 | 42 | ## 2. The Web Server 43 | 44 | \[missing\] describes the Racket web server, which supports servlets 45 | implemented in Racket. 46 | 47 | ## 3. Using Foreign Libraries 48 | 49 | \[missing\] describes tools for using Racket to access libraries that 50 | are normally used by C programs. 51 | 52 | ## 4. And More 53 | 54 | [Racket Documentation](../index.html) lists documentation for many other 55 | installed libraries. Run `raco docs` to find documentation for libraries 56 | that are installed on your system and specific to your user account. 57 | 58 | [The Racket package repository](https://pkgs.racket-lang.org/) offer 59 | even more downloadable packages that are contributed by Racketeers. 60 | 61 | The legacy [PLaneT](http://planet.racket-lang.org/) site offers 62 | additional packages, although maintained packages have generally 63 | migrated to the newer package repository. 64 | -------------------------------------------------------------------------------- /qtest/mds/paths.md: -------------------------------------------------------------------------------- 1 | # Paths 2 | 3 | A _path_ encapsulates a filesystem path that \(potentially\) names a 4 | file or directory. Although paths can be converted to and from strings 5 | and byte strings, neither strings nor byte strings are suitable for 6 | representing general paths. The problem is that paths are represented in 7 | the filesystem as either byte sequences or UTF-16 sequences \(depending 8 | on the operating systems\); the sequences are not always human-readable, 9 | and not all sequences can be decoded to Unicode scalar values. 10 | 11 | Despite the occasional encoding problems, most paths can be converted to 12 | and from strings. Thus, procedures that accept a path argument always 13 | accept a string, and the printed form of a path uses the string decoding 14 | of the path inside `#`. The `display` form of a path is 15 | the same as the `display` form of its string encodings. 16 | 17 | Examples: 18 | 19 | ```racket 20 | > (string->path "my-data.txt") 21 | # 22 | > (file-exists? "my-data.txt") 23 | #f 24 | > (file-exists? (string->path "my-data.txt")) 25 | #f 26 | > (display (string->path "my-data.txt")) 27 | my-data.txt 28 | ``` 29 | 30 | Procedures that produce references to the filesystem always produce path 31 | values, instead of strings. 32 | 33 | Example: 34 | 35 | ```racket 36 | > (path-replace-suffix "foo.scm" #".rkt") 37 | # 38 | ``` 39 | 40 | Although it’s sometimes tempting to directly manipulate strings that 41 | represent filesystem paths, correctly manipulating a path can be 42 | surprisingly difficult. Windows path manipulation is especially tricky, 43 | because path elements like `"aux"` can have special meanings. 44 | 45 | > +\[missing\] in \[missing\] documents the fine points of Windows 46 | > filesystem paths. 47 | 48 | Use procedures like `split-path` and `build-path` to deconstruct and 49 | construct paths. When you must manipulate the name of a specific path 50 | element \(i.e., a file or directory component in a path\), use 51 | procedures like `path-element->bytes` and `bytes->path-element`. 52 | 53 | Examples: 54 | 55 | ```racket 56 | > (build-path "easy" "file.rkt") 57 | # 58 | > (split-path (build-path "easy" "file.rkt")) 59 | # 60 | # 61 | #f 62 | ``` 63 | -------------------------------------------------------------------------------- /qtest/mds/places.md: -------------------------------------------------------------------------------- 1 | # Parallelism with Places 2 | 3 | The `racket/place` library provides support for performance improvement 4 | through parallelism with the `place` form. The `place` form creates a 5 | _place_, which is effectively a new Racket instance that can run in 6 | parallel to other places, including the initial place. The full power 7 | of the Racket language is available at each place, but places can 8 | communicate only through message passing—using the `place-channel-put` 9 | and `place-channel-get` functions on a limited set of values—which helps 10 | ensure the safety and independence of parallel computations. 11 | 12 | As a starting example, the racket program below uses a place to 13 | determine whether any number in the list has a double that is also in 14 | the list: 15 | 16 | ```racket 17 | #lang racket 18 | 19 | (provide main) 20 | 21 | (define (any-double? l) 22 | (for/or ([i (in-list l)]) 23 | (for/or ([i2 (in-list l)]) 24 | (= i2 (* 2 i))))) 25 | 26 | (define (main) 27 | (define p 28 | (place ch 29 | (define l (place-channel-get ch)) 30 | (define l-double? (any-double? l)) 31 | (place-channel-put ch l-double?))) 32 | 33 | (place-channel-put p (list 1 2 4 8)) 34 | 35 | (place-channel-get p)) 36 | ``` 37 | 38 | The identifier `ch` after `place` is bound to a _place channel_. The 39 | remaining body expressions within the `place` form are evaluated in a 40 | new place, and the body expressions use `ch` to communicate with the 41 | place that spawned the new place. 42 | 43 | In the body of the `place` form above, the new place receives a list of 44 | numbers over `ch` and binds the list to `l`. It then calls 45 | `any-double?` on the list and binds the result to `l-double?`. The final 46 | body expression sends the `l-double?` result back to the original place 47 | over `ch`. 48 | 49 | In DrRacket, after saving and running the above program, evaluate 50 | `(main)` in the interactions window to create the new place. When using 51 | places inside DrRacket, the module containg place code must be saved to 52 | a file before it will execute. Alternatively, save the program as 53 | `"double.rkt"` and run from a command line with 54 | 55 |   `racket -tm double.rkt` 56 | 57 | where the `-t` flag tells `racket` to load the `double.rkt` module, the 58 | `-m` flag calls the exported `main` function, and `-tm` combines the two 59 | flags. 60 | 61 | The `place` form has two subtle features. First, it lifts the `place` 62 | body to an anonymous, module-level function. This lifting means that 63 | any binding referenced by the `place` body must be available in the 64 | module’s top level. Second, the `place` form `dynamic-require`s the 65 | enclosing module in a newly created place. As part of the 66 | `dynamic-require`, the current module body is evaluated in the new 67 | place. The consequence of this second feature is that `place` should 68 | not appear immediately in a module or in a function that is called in a 69 | module’s top level; otherwise, invoking the module will invoke the same 70 | module in a new place, and so on, triggering a cascade of place 71 | creations that will soon exhaust memory. 72 | 73 | ```racket 74 | #lang racket 75 | 76 | (provide main) 77 | 78 | ; Don't do this! 79 | (define p (place ch (place-channel-get ch))) 80 | 81 | (define (indirect-place-invocation) 82 | (define p2 (place ch (place-channel-get ch)))) 83 | 84 | ; Don't do this, either! 85 | (indirect-place-invocation) 86 | ``` 87 | -------------------------------------------------------------------------------- /qtest/mds/ports.md: -------------------------------------------------------------------------------- 1 | # Input and Output Ports 2 | 3 | A _port_ encapsulates an I/O stream, normally for just one direction. An 4 | _input port_ reads from a stream, and an _output port_ writes to a 5 | string. 6 | 7 | For many procedures that accept a port argument, the argument is 8 | optional, and it defaults to either the _current input port_ or _current 9 | output port_. For `mzscheme`, the current ports are initialized to the 10 | process’s stdin and stdout. The `current-input-port` and 11 | `current-output-port` procedures, when called with no arguments, return 12 | the current output and input port, respectively. 13 | 14 | Examples: 15 | 16 | ```racket 17 | > (display "hello world\n") 18 | hello world 19 | > (display "hello world\n" (current-output-port)) 20 | hello world 21 | ``` 22 | 23 | Ports are created by various procedures that specific to the different 24 | kinds of streams. For example, `open-input-file` creates an input port 25 | for reading from a file. Procedures like `with-input-from-file` both 26 | create a port and install it as the current port while calling a given 27 | body procedure. 28 | 29 | See \[missing\] for information about using ports. 30 | -------------------------------------------------------------------------------- /qtest/mds/quote.md: -------------------------------------------------------------------------------- 1 | # Quoting: `quote` and `'` 2 | 3 | > +\[missing\] in \[missing\] also documents `quote`. 4 | 5 | The `quote` form produces a constant: 6 | 7 | ```racket 8 | (quote datum) 9 | ``` 10 | 11 | The syntax of a `datum` is technically specified as anything that the 12 | `read` function parses as a single element. The value of the `quote` 13 | form is the same value that `read` would produce given `datum`. 14 | 15 | The `datum` can be a symbol, a boolean, a number, a \(character or 16 | byte\) string, a character, a keyword, an empty list, a pair \(or list\) 17 | containing more such values, a vector containing more such values, a 18 | hash table containing more such values, or a box containing another such 19 | value. 20 | 21 | Examples: 22 | 23 | ```racket 24 | > (quote apple) 25 | 'apple 26 | > (quote #t) 27 | #t 28 | > (quote 42) 29 | 42 30 | > (quote "hello") 31 | "hello" 32 | > (quote ()) 33 | '() 34 | > (quote ((1 2 3) #("z" x) . the-end)) 35 | '((1 2 3) #("z" x) . the-end) 36 | > (quote (1 2 . (3))) 37 | '(1 2 3) 38 | ``` 39 | 40 | As the last example above shows, the `datum` does not have to match the 41 | normalized printed form of a value. A `datum` cannot be a printed 42 | representation that starts with `#<`, so it cannot be `#`, 43 | `#`, or a procedure. 44 | 45 | The `quote` form is rarely used for a `datum` that is a boolean, number, 46 | or string by itself, since the printed forms of those values can already 47 | be used as constants. The `quote` form is more typically used for 48 | symbols and lists, which have other meanings \(identifiers, function 49 | calls, etc.\) when not quoted. 50 | 51 | An expression 52 | 53 | ```racket 54 | 'datum 55 | ``` 56 | 57 | is a shorthand for 58 | 59 | `(quote` `datum)` 60 | 61 | and this shorthand is almost always used instead of `quote`. The 62 | shorthand applies even within the `datum`, so it can produce a list 63 | containing `quote`. 64 | 65 | > +\[missing\] in \[missing\] provides more on the `'` shorthand. 66 | 67 | Examples: 68 | 69 | ```racket 70 | > 'apple 71 | 'apple 72 | > '"hello" 73 | "hello" 74 | > '(1 2 3) 75 | '(1 2 3) 76 | > (display '(you can 'me)) 77 | (you can (quote me)) 78 | ``` 79 | -------------------------------------------------------------------------------- /qtest/mds/regexps-data.md: -------------------------------------------------------------------------------- 1 | # Regexps 2 | 3 | A _regexp_ is a pattern for regular-expression matching. 4 | -------------------------------------------------------------------------------- /qtest/mds/scripts.md: -------------------------------------------------------------------------------- 1 | # Scripts 2 | 3 | Racket files can be turned into executable scripts on Unix and Mac OS. 4 | On Windows, a compatibility layer like Cygwin support the same kind of 5 | scripts, or scripts can be implemented as batch files. 6 | 7 | ## 1. Unix Scripts 8 | 9 | In a Unix environment \(including Linux and Mac OS\), a Racket file can 10 | be turned into an executable script using the shell’s `#!` convention. 11 | The first two characters of the file must be `#!`; the next character 12 | must be either a space or `/`, and the remainder of the first line must 13 | be a command to execute the script. For some platforms, the total length 14 | of the first line is restricted to 32 characters, and sometimes the 15 | space is required. 16 | 17 | > Use `#lang` `racket/base` instead of `#lang` `racket` to produce scripts 18 | > with a faster startup time. 19 | 20 | The simplest script format uses an absolute path to a `racket` 21 | executable followed by a module declaration. For example, if `racket` is 22 | installed in `"/usr/local/bin"`, then a file containing the following 23 | text acts as a “hello world” script: 24 | 25 | `#! /usr/local/bin/racket` 26 | `#lang racket/base` 27 | `"Hello, world!"` 28 | 29 | In particular, if the above is put into a file `"hello"` and the file is 30 | made executable \(e.g., with `chmod a+x hello`\), then typing `./hello` 31 | at the shell prompt produces the output `"Hello, world!"`. 32 | 33 | The above script works because the operating system automatically puts 34 | the path to the script as the argument to the program started by the 35 | `#!` line, and because `racket` treats a single non-flag argument as a 36 | file containing a module to run. 37 | 38 | Instead of specifying a complete path to the `racket` executable, a 39 | popular alternative is to require that `racket` is in the user’s command 40 | path, and then “trampoline” using `/usr/bin/env`: 41 | 42 | `#! /usr/bin/env racket` 43 | `#lang racket/base` 44 | `"Hello, world!"` 45 | 46 | In either case, command-line arguments to a script are available via 47 | `current-command-line-arguments`: 48 | 49 | `#! /usr/bin/env racket` 50 | `#lang racket/base` 51 | `(printf "Given arguments: ~s\n"` 52 | `(current-command-line-arguments))` 53 | 54 | If the name of the script is needed, it is available via 55 | `(find-system-path 'run-file)`, instead of via 56 | `(current-command-line-arguments)`. 57 | 58 | Usually, the best way to handle command-line arguments is to parse them 59 | using the `command-line` form provided by `racket`. The `command-line` 60 | form extracts command-line arguments from 61 | `(current-command-line-arguments)` by default: 62 | 63 | `#! /usr/bin/env racket` 64 | `#lang racket` 65 | 66 | `(define verbose? (make-parameter #f))` 67 | 68 | `(define greeting` 69 | `(command-line` 70 | `#:once-each` 71 | `[("-v") "Verbose mode" (verbose? #t)]` 72 | `#:args` 73 | `(str) str))` 74 | 75 | `(printf "~a~a\n"` 76 | `greeting` 77 | `(if (verbose?) " to you, too!" ""))` 78 | 79 | Try running the above script with the `--help` flag to see what 80 | command-line arguments are allowed by the script. 81 | 82 | An even more general trampoline uses `/bin/sh` plus some lines that are 83 | comments in one language and expressions in the other. This trampoline 84 | is more complicated, but it provides more control over command-line 85 | arguments to `racket`: 86 | 87 | `#! /bin/sh` 88 | `#|` 89 | `exec racket -e '(printf "Running...\n")' -u "$0" ${1+"$@"}` 90 | `|#` 91 | `#lang racket/base` 92 | `(printf "The above line of output had been produced via\n")` 93 | `(printf "a use of the `-e' flag.\n")` 94 | `(printf "Given arguments: ~s\n"` 95 | `(current-command-line-arguments))` 96 | 97 | Note that `#!` starts a line comment in Racket, and `#|`...`|#` forms a 98 | block comment. Meanwhile, `#` also starts a shell-script comment, while 99 | `exec racket` aborts the shell script to start `racket`. That way, the 100 | script file turns out to be valid input to both `/bin/sh` and `racket`. 101 | 102 | ## 2. Windows Batch Files 103 | 104 | A similar trick can be used to write Racket code in Windows `.bat` batch 105 | files: 106 | 107 | `; @echo off` 108 | `; Racket.exe "%~f0" %*` 109 | `; exit /b` 110 | `#lang racket/base` 111 | `"Hello, world!"` 112 | -------------------------------------------------------------------------------- /qtest/mds/simple-data.md: -------------------------------------------------------------------------------- 1 | # Simple Values 2 | 3 | Racket values include numbers, booleans, strings, and byte strings. In 4 | DrRacket and documentation examples \(when you read the documentation in 5 | color\), value expressions are shown in green. 6 | 7 | _Numbers_ are written in the usual way, including fractions and 8 | imaginary numbers: 9 | 10 | > +\[missing\] \(later in this guide\) explains more about numbers. 11 | 12 | ```racket 13 | 1 3.14 14 | 1/2 6.02e+23 15 | 1+2i 9999999999999999999999 16 | ``` 17 | 18 | _Booleans_ are `#t` for true and `#f` for false. In conditionals, 19 | however, all non-`#f` values are treated as true. 20 | 21 | > +\[missing\] \(later in this guide\) explains more about booleans. 22 | 23 | _Strings_ are written between doublequotes. Within a string, backslash 24 | is an escaping character; for example, a backslash followed by a 25 | doublequote includes a literal doublequote in the string. Except for an 26 | unescaped doublequote or backslash, any Unicode character can appear in 27 | a string constant. 28 | 29 | > +\[missing\] \(later in this guide\) explains more about strings. 30 | 31 | ```racket 32 | "Hello, world!" 33 | "Benjamin \"Bugsy\" Siegel" 34 | "λx:(μα.α→α).xx" 35 | ``` 36 | 37 | When a constant is evaluated in the REPL, it typically prints the same 38 | as its input syntax. In some cases, the printed form is a normalized 39 | version of the input syntax. In documentation and in DrRacket’s REPL, 40 | results are printed in blue instead of green to highlight the difference 41 | between an input expression and a printed result. 42 | 43 | Examples: 44 | 45 | ```racket 46 | > 1.0000 47 | 1.0 48 | > "Bugs \u0022Figaro\u0022 Bunny" 49 | "Bugs \"Figaro\" Bunny" 50 | ``` 51 | -------------------------------------------------------------------------------- /qtest/mds/symbols.md: -------------------------------------------------------------------------------- 1 | # Symbols 2 | 3 | A _symbol_ is an atomic value that prints like an identifier preceded 4 | with `'`. An expression that starts with `'` and continues with an 5 | identifier produces a symbol value. 6 | 7 | Examples: 8 | 9 | ```racket 10 | > 'a 11 | 'a 12 | > (symbol? 'a) 13 | #t 14 | ``` 15 | 16 | For any sequence of characters, exactly one corresponding symbol is 17 | _interned_; calling the `string->symbol` procedure, or `read`ing a 18 | syntactic identifier, produces an interned symbol. Since interned 19 | symbols can be cheaply compared with `eq?` \(and thus `eqv?` or 20 | `equal?`\), they serve as a convenient values to use for tags and 21 | enumerations. 22 | 23 | Symbols are case-sensitive. By using a `#ci` prefix or in other ways, 24 | the reader can be made to case-fold character sequences to arrive at a 25 | symbol, but the reader preserves case by default. 26 | 27 | Examples: 28 | 29 | ```racket 30 | > (eq? 'a 'a) 31 | #t 32 | > (eq? 'a (string->symbol "a")) 33 | #t 34 | > (eq? 'a 'b) 35 | #f 36 | > (eq? 'a 'A) 37 | #f 38 | > #ci'A 39 | 'a 40 | ``` 41 | 42 | Any string \(i.e., any character sequence\) can be supplied to 43 | `string->symbol` to obtain the corresponding symbol. For reader input, 44 | any character can appear directly in an identifier, except for 45 | whitespace and the following special characters: 46 | 47 |    `(` `)` `[` `]` `{` `}` `"` `,` `'` ` `;` `#` `|` `\` 48 | 49 | Actually, `#` is disallowed only at the beginning of a symbol, and then 50 | only if not followed by `%`; otherwise, `#` is allowed, too. Also, `.` 51 | by itself is not a symbol. 52 | 53 | Whitespace or special characters can be included in an identifier by 54 | quoting them with `|` or `\`. These quoting mechanisms are used in the 55 | printed form of identifiers that contain special characters or that 56 | might otherwise look like numbers. 57 | 58 | Examples: 59 | 60 | ```racket 61 | > (string->symbol "one, two") 62 | '|one, two| 63 | > (string->symbol "6") 64 | '|6| 65 | ``` 66 | 67 | > +\[missing\] in \[missing\] documents the fine points of the syntax of 68 | > symbols. 69 | 70 | The `write` function prints a symbol without a `'` prefix. The `display` 71 | form of a symbol is the same as the corresponding string. 72 | 73 | Examples: 74 | 75 | ```racket 76 | > (write 'Apple) 77 | Apple 78 | > (display 'Apple) 79 | Apple 80 | > (write '|6|) 81 | |6| 82 | > (display '|6|) 83 | 6 84 | ``` 85 | 86 | The `gensym` and `string->uninterned-symbol` procedures generate fresh 87 | _uninterned_ symbols that are not equal \(according to `eq?`\) to any 88 | previously interned or uninterned symbol. Uninterned symbols are useful 89 | as fresh tags that cannot be confused with any other value. 90 | 91 | Examples: 92 | 93 | ```racket 94 | > (define s (gensym)) 95 | > s 96 | 'g42 97 | > (eq? s 'g42) 98 | #f 99 | > (eq? 'a (string->uninterned-symbol "a")) 100 | #f 101 | ``` 102 | 103 | > +\[missing\] in \[missing\] provides more on symbols. 104 | -------------------------------------------------------------------------------- /qtest/mds/vectors.md: -------------------------------------------------------------------------------- 1 | # Vectors 2 | 3 | A _vector_ is a fixed-length array of arbitrary values. Unlike a list, a 4 | vector supports constant-time access and update of its elements. 5 | 6 | A vector prints similar to a list—as a parenthesized sequence of its 7 | elements—but a vector is prefixed with `#` after `'`, or it uses 8 | `vector` if one of its elements cannot be expressed with `quote`. 9 | 10 | For a vector as an expression, an optional length can be supplied. Also, 11 | a vector as an expression implicitly `quote`s the forms for its content, 12 | which means that identifiers and parenthesized forms in a vector 13 | constant represent symbols and lists. 14 | 15 | > +\[missing\] in \[missing\] documents the fine points of the syntax of 16 | > vectors. 17 | 18 | Examples: 19 | 20 | ```racket 21 | > #("a" "b" "c") 22 | '#("a" "b" "c") 23 | > #(name (that tune)) 24 | '#(name (that tune)) 25 | > #4(baldwin bruce) 26 | '#(baldwin bruce bruce bruce) 27 | > (vector-ref #("a" "b" "c") 1) 28 | "b" 29 | > (vector-ref #(name (that tune)) 1) 30 | '(that tune) 31 | ``` 32 | 33 | Like strings, a vector is either mutable or immutable, and vectors 34 | written directly as expressions are immutable. 35 | 36 | Vectors can be converted to lists and vice versa via `vector->list` and 37 | `list->vector`; such conversions are particularly useful in combination 38 | with predefined procedures on lists. When allocating extra lists seems 39 | too expensive, consider using looping forms like `for/fold`, which 40 | recognize vectors as well as lists. 41 | 42 | Example: 43 | 44 | ```racket 45 | > (list->vector (map string-titlecase 46 | (vector->list #("three" "blind" "mice")))) 47 | '#("Three" "Blind" "Mice") 48 | ``` 49 | 50 | > +\[missing\] in \[missing\] provides more on vectors and vector 51 | > procedures. 52 | -------------------------------------------------------------------------------- /qtest/mds/void-and-undef.md: -------------------------------------------------------------------------------- 1 | # Void and Undefined 2 | 3 | Some procedures or expression forms have no need for a result value. For 4 | example, the `display` procedure is called only for the side-effect of 5 | writing output. In such cases the result value is normally a special 6 | constant that prints as `#`. When the result of an expression is 7 | simply `#`, the REPL does not print anything. 8 | 9 | The `void` procedure takes any number of arguments and returns 10 | `#`. \(That is, the identifier `void` is bound to a procedure that 11 | returns `#`, instead of being bound directly to `#`.\) 12 | 13 | Examples: 14 | 15 | ```racket 16 | > (void) 17 | > (void 1 2 3) 18 | > (list (void)) 19 | '(#) 20 | ``` 21 | 22 | The `undefined` constant, which prints as `#`, is sometimes 23 | used as the result of a reference whose value is not yet available. In 24 | previous versions of Racket \(before version 6.1\), referencing a local 25 | binding too early produced `#`; too-early references now 26 | raise an exception, instead. 27 | 28 | > The `undefined` result can still be produced in some cases by the 29 | > `shared` form. 30 | 31 | ```racket 32 | (define (fails) 33 | (define x x) 34 | x) 35 | ``` 36 | 37 | ```racket 38 | > (fails) 39 | x: undefined; 40 | cannot use before initialization 41 | ``` 42 | -------------------------------------------------------------------------------- /qtest/mydraw.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter 2 | 3 | '(q ((page-width "4in")(page-height "4in")) 4 | 5 | "Hello world" 6 | 7 | (q ((draw "line") (x1 "10") (y1 "10") (x2 "100") (y2 "200"))) 8 | (q ((draw "line") (x1 "100") (y1 "200") (x2 "200") (y2 "100"))) 9 | 10 | (q ((break "page"))) 11 | 12 | "Goodbye fools" 13 | 14 | (q ((draw "text")(font-italic "true")(x "20")(y "20")(text "Hello from 20,20")) "outside") 15 | 16 | ) -------------------------------------------------------------------------------- /qtest/paths-to-test.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/runtime-path 3 | racket/match 4 | quadwriter) 5 | (provide (all-defined-out)) 6 | 7 | (define-runtime-path here ".") 8 | 9 | (define (find-test-paths) 10 | (for/list ([test-path (in-directory here)] 11 | #:when (regexp-match #rx"test-.*.rkt$" (path->string test-path))) 12 | test-path)) 13 | 14 | 15 | (define (test-pdf-name path) 16 | (path-add-extension (path-replace-extension path #".pdf") #"" #"-tester.")) 17 | 18 | (define (update-test-pdf test-path-arg) 19 | (define test-path (match test-path-arg 20 | [(? absolute-path? ap) ap] 21 | [rp (build-path here rp)])) 22 | (define-values (dir name _) (split-path test-path)) 23 | (displayln (path->string name)) 24 | (time (parameterize ([quadwriter-test-mode #t]) 25 | (render-pdf (dynamic-require test-path 'doc) (test-pdf-name test-path) test-path)))) -------------------------------------------------------------------------------- /qtest/raco.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | (require racket/logging 3 | racket/match 4 | "log.rkt") 5 | 6 | (with-logging-to-port 7 | (current-error-port) 8 | (λ () 9 | (match (with-handlers ([exn:fail? (λ (exn) #f)]) 10 | (vector-ref (current-command-line-arguments) 0)) 11 | ["test" (dynamic-require 'qtest/all-tests #f)] 12 | ["update" (dynamic-require 'qtest/update-tests #f)] 13 | [_ (displayln "no cmd given")])) 14 | #:logger qtest-logger 15 | 'info 16 | 'qtest) -------------------------------------------------------------------------------- /qtest/sample-main.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter/markup 2 | 3 | ◊h2{yes} 4 | 5 | Hello ◊b{world} you are my 6 | 7 | ◊em{power animal} -------------------------------------------------------------------------------- /qtest/test-adjustment-sizing-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-adjustment-sizing-tester.pdf -------------------------------------------------------------------------------- /qtest/test-adjustment-sizing.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter 2 | 3 | 4 | '(q ((column-count "2")(font-size "18")(line-height "2em")) "The lines in both columns should be vertically aligned. The column on the left is font size 18pt with line height 2em, or 36pt. The right column is font size 50%, or 9pt, with line height 4em, or 36pt." 5 | (column-break) 6 | (para-break) 7 | (q ((font-size "0.5em")(line-height "4em")) "The lines in both columns should be vertically aligned. The column on the left is font size 18pt with line height 2em, or 36pt. The right column is font size 50%, or 9pt, with line height 4em, or 36pt.")) -------------------------------------------------------------------------------- /qtest/test-baseline-shift-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-baseline-shift-tester.pdf -------------------------------------------------------------------------------- /qtest/test-baseline-shift.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter 2 | 3 | '(q "No shift " (q ((font-baseline-shift "3")) "sherlock.") " Thank you.") -------------------------------------------------------------------------------- /qtest/test-breaks-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-breaks-tester.pdf -------------------------------------------------------------------------------- /qtest/test-breaks.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter 2 | 3 | #:footer-display "true" 4 | 5 | '(q ((page-height "6in")(column-count "2")) 6 | 7 | "Page 1 Column 1 Line 1" 8 | 9 | (line-break) 10 | 11 | "Page 1 Column 1 Line 2" 12 | 13 | (column-break) 14 | 15 | (para-break) 16 | 17 | "Page 1 Column 2 Line 1" 18 | 19 | (line-break) 20 | 21 | "Page 1 Column 2 Line 2" 22 | 23 | (page-break) 24 | 25 | 26 | "Page 2 Column 1 Line 1" 27 | 28 | (line-break) 29 | 30 | (column-break) 31 | 32 | (page-break) 33 | 34 | 35 | "Page 3 Column 1 Line 1" 36 | 37 | (page-break) 38 | 39 | (page-break) 40 | 41 | "Page 5 Column 1 Line 1" 42 | 43 | ) -------------------------------------------------------------------------------- /qtest/test-docs-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-docs-tester.pdf -------------------------------------------------------------------------------- /qtest/test-docs.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter/markdown 2 | 3 | #:line-wrap "best" 4 | #:footer-display "true" 5 | 6 | # Hyphenate 7 | 8 | A simple _hyphenation engine_ that uses the Knuth–Liang hyphenation algorithm originally developed for TeX. 9 | 10 | I **have added little** to their work. Accordingly, I take no credit, except a spoonful of *snako-bits.* 11 | 12 | And now, for something __altogether__ the same. Yes! No?!ß 13 | 14 | ## 1. Installation 15 | 16 | At the command line: 17 | 18 | We said `raco pkg install hyphenate` dude 19 | 20 | All right then. 21 | 22 | A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 23 | Certain word processors allow users to [insert soft 24 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 25 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 26 | Certain word processors allow users to [insert soft 27 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 28 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 29 | Certain word processors allow users to [insert soft 30 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 31 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 32 | Certain word processors allow users to [insert soft 33 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 34 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 35 | Certain word processors allow users to [insert soft 36 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 37 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 38 | Certain word processors allow users to [insert soft 39 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 40 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 41 | Certain word processors allow users to [insert soft 42 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 43 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 44 | Certain word processors allow users to [insert soft 45 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 46 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 47 | Certain word processors allow users to [insert soft 48 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 49 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 50 | Certain word processors allow users to [insert soft 51 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 52 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 53 | Certain word processors allow users to [insert soft 54 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 55 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 56 | Certain word processors allow users to [insert soft 57 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 58 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 59 | Certain word processors allow users to [insert soft 60 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 61 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 62 | Certain word processors allow users to [insert soft 63 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 64 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 65 | Certain word processors allow users to [insert soft 66 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 67 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 68 | Certain word processors allow users to [insert soft 69 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 70 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 71 | Certain word processors allow users to [insert soft 72 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 73 | text. A [list of web colors](https://en.wikipedia.org/wiki/Web_colors). 74 | Certain word processors allow users to [insert soft 75 | hyphens](http://practicaltypography.com/optional-hyphens.html) in their 76 | text. 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /qtest/test-emoji-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-emoji-tester.pdf -------------------------------------------------------------------------------- /qtest/test-emoji.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter/markdown 2 | 3 | Keycap digits & flags don't work because in Noto Emoji, they are encoded using older PUA codepoints rather than the Unicode (Noto Emoji has not been updated in 4 yrs) 4 | 5 | 󾠬 󾠷 󾠮 󾠯 󾠰 󾠱 󾠲 󾠳 󾠴 󾠵 󾠶  6 | 7 | 󾓥󾓦󾓧󾓨󾓩󾓪󾓫󾓬󾓭 8 | 9 | ℹ ↔ ↕ ↖ ↗ ↘ ↙ ↩ ↪ ⌚ ⌛ ⏩ ⏪ ⏫ ⏬ ⏰ ⏳ Ⓜ ▪ ▫ ▶ ◀ ◊ ◻ ◼ ◽ ◾ ☀ ☁ ☺ ♈ ♉ ♊ ♋ ♌ ♍ ♎ ♏ ♐ ♑ ♒ ♓ ♠ ♣ ♥ ♦ ♨ ♻ ♿ ⚓ ⚠ ⚡ ⚪ ⚫ ⚽ ⚾ ⛄ ⛅ ⛎ ⛔ ⛪ ⛲ ⛳ ⛵ ⛺ ⛽ ✂ ✅ ✈ ✉ ✊ ✋ ✌ ✏ ✒ ✔ ✖ ✨ ✳ ✴ ❄ ❇ ❌ ❎ ❓ ❔ ❕ ❗ ❤ ➕ ➖ ➗ ➡ ➰ ➿ ⤴ ⤵ ⬅ ⬆ ⬇ ⬛ ⬜ ⭐ ⭕ 〰 〽 ㊗ ㊙ 🀄 🃏 🅰 🅱 🅾 🅿 🆎 🆑 🆒 🆓 🆔 🆕 🆖 🆗 🆘 🆙 🆚 🇦 🇧 🇨 🇩 🇪 🇫 🇬 🇭 🇮 🇯 🇰 🇱 🇲 🇳 🇴 🇵 🇶 🇷 🇸 🇹 🇺 🇻 🇼 🇽 🇾 🇿 🈁 🈂 🈚 🈯 🈲 🈳 🈴 🈵 🈶 🈷 🉑 🌀 🌁 🌂 🌃 🌄 🌅 🌆 🌇 🌈 🌉 🌊 🌋 🌌 🌏 🌑 🌓 🌔 🌕 🌙 🌛 🌟 🌠 🌰 🌱 🌴 🌵 🌷 🌸 🌹 🌺 🌻 🌼 🌽 🌾 🌿 🍀 🍁 🍂 🍃 🍄 🍅 🍆 🍇 🍈 🍉 🍊 🍌 🍍 🍎 🍏 🍑 🍒 🍓 🍔 🍕 🍖 🍗 🍘 🍙 🍚 🍛 🍜 🍝 🍞 🍟 🍠 🍡 🍢 🍣 🍤 🍥 🍦 🍧 🍨 🍩 🍪 🍫 🍬 🍭 🍮 🍯 🍰 🍱 🍲 🍳 🍴 🍵 🍶 🍷 🍸 🍹 🍺 🍻 🎀 🎁 🎂 🎃 🎄 🎅 🎆 🎇 🎈 🎉 🎊 🎋 🎌 🎍 🎎 🎏 🎐 🎑 🎒 🎓 🎠 🎡 🎢 🎣 🎤 🎥 🎦 🎧 🎨 🎩 🎪 🎫 🎬 🎭 🎮 🎯 🎰 🎱 🎲 🎳 🎴 🎵 🎶 🎷 🎸 🎹 🎺 🎻 🎼 🎽 🎾 🎿 🏀 🏁 🏂 🏃 🏄 🏆 🏈 🏊 🏠 🏡 🏢 🏣 🏥 🏦 🏧 🏨 🏩 🏪 🏫 🏬 🏭 🏮 🏯 🏰 🐌 🐍 🐎 🐑 🐒 🐔 🐗 🐘 🐙 🐚 🐛 🐜 🐝 🐞 🐟 🐠 🐡 🐢 🐣 🐤 🐥 🐦 🐧 🐨 🐩 🐫 🐬 🐭 🐮 🐯 🐰 🐱 🐲 🐳 🐴 🐵 🐶 🐷 🐸 🐹 🐺 🐻 🐼 🐽 🐾 👀 👂 👃 👄 👅 👆 👇 👈 👉 👊 👋 👌 👍 👎 👏 👐 👑 👒 👓 👔 👕 👖 👗 👘 👙 👚 👛 👜 👝 👞 👟 👠 👡 👢 👣 👤 👦 👧 👨 👩 👪 👫 👮 👯 👰 👴 👵 👶 👷 👸 👹 👺 👻 👼 👽 👾 👿 💀 💁 💂 💃 💄 💅 💆 💇 💈 💉 💊 💋 💌 💍 💎 💏 💐 💑 💒 💓 💔 💕 💖 💗 💘 💙 💚 💛 💜 💝 💞 💟 💠 💡 💢 💣 💤 💥 💦 💧 💨 💩 💪 💫 💬 💮 💯 💰 💱 💲 💳 💴 💵 💸 💹 💺 💻 💼 💽 💾 💿 📀 📁 📂 📃 📄 📅 📆 📇 📈 📉 📊 📋 📌 📍 📎 📏 📐 📑 📒 📓 📔 📕 📖 📗 📘 📙 📚 📛 📜 📝 📞 📟 📠 📡 📢 📣 📤 📥 📦 📧 📨 📩 📪 📫 📮 📰 📱 📲 📳 📴 📶 📷 📹 📺 📻 📼 🔃 🔊 🔋 🔌 🔍 🔎 🔏 🔐 🔑 🔒 🔓 🔔 🔖 🔗 🔘 🔙 🔚 🔛 🔜 🔝 🔞 🔟 🔠 🔡 🔢 🔣 🔤 🔥 🔦 🔧 🔨 🔩 🔪 🔫 🔮 🔯 🔰 🔱 🔲 🔳 🔴 🔵 🔶 🔷 🔸 🔹 🔺 🔻 🔼 🔽 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🗻 🗼 🗽 🗾 🗿 😁 😂 😃 😄 😅 😆 😉 😊 😋 😌 😍 😏 😒 😓 😔 😖 😘 😚 😜 😝 😞 😠 😡 😢 😣 😤 😥 😨 😩 😪 😫 😭 😰 😱 😲 😳 😵 😷 😸 😹 😺 😻 😼 😽 😾 😿 🙀 🙅 🙆 🙇 🙈 🙉 🙊 🙋 🙌 🙍 🙎 🙏 🚀 🚃 🚄 🚅 🚇 🚉 🚌 🚏 🚑 🚒 🚓 🚕 🚗 🚙 🚚 🚢 🚤 🚥 🚧 🚨 🚩 🚪 🚫 🚬 🚭 🚲 🚶 🚹 🚺 🚻 🚼 🚽 🚾 🛀 🈸 🈹 🈺 🉐 🏇 🏉 🏤 💭 💶 💷 📬 📭 📯 📵 🚠 🚡 🚣 🚦 🚮 🚯 🚰 🚱 🚳 🚴 🚵 🚷 🚸 🚿 🛁 🛂 🛃 🛄 🛅 🌍 🌎 🌚 🌜 🌝 🌞 🍋 🍼 🐊 🐋 🐏 👬 👭 🔬 🔭 🕜 🕝 🕞 🕟 😎 🚊 🚋 🚍 🚎 🚛 🚜 🚝 🚞 🚟 🌐 🌒 🌖 🌗 🌘 🌲 🌳 🍐 🐀 🐁 🐂 🐃 🐄 🐅 🐆 🐇 🐈 🐉 🐐 🐓 🐕 🐖 👥 👱 👲 👳 🔀 🔁 🔂 🔄 🔅 🔆 🔇 🔈 🔉 🔕 🕠 🕡 🕢 🕣 🕤 🕥 🕦 🕧 😇 😈 😐 😶 🚆 🚈 🚐 🚔 🚖 ☎ ☝ ☑ ☔ ☕ 😛 😟 😬 😮 😯 😀 😑 😕 😗 😙 😦 😧 😴 🚁 🚂 -------------------------------------------------------------------------------- /qtest/test-fallback-mini-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-fallback-mini-tester.pdf -------------------------------------------------------------------------------- /qtest/test-fallback-mini.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter/markdown 2 | 3 | 😂 Hel😂lo 😂a😂b😂c😂d😂e😂😂 4 | 5 | ԢHelԢloԢ -------------------------------------------------------------------------------- /qtest/test-fallback-super-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-fallback-super-tester.pdf -------------------------------------------------------------------------------- /qtest/test-fallback-super.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter/markdown 2 | 3 | Ѧ _Ѧ_ __Ѧ__ *__Ѧ__* 4 | 5 | 6 | ǰ ʼ ϑ ϒ ϖ Ḿ ḿ Ḁ ḁ ˳ Ơ ơ Ư ư Ѐ Ѝ ѐ ѝ Ѡ ѡ Ѣ ѣ Ѥ ѥ Ѧ ѧ Ѩ ѩ Ѫ ѫ Ѭ ѭ Ѯ ѯ Ѱ ѱ Ѳ ѳ Ѵ ѵ Ѷ ѷ Ѹ ѹ Ѻ ѻ Ѽ ѽ Ѿ ѿ Ҁ ҁ ҂ ҈ ҉ Ҋ ҋ Ҍ ҍ Ҏ ҏ Ғ ғ Ҕ ҕ Җ җ Ҙ ҙ Қ қ Ҝ ҝ Ҟ ҟ Ҡ ҡ Ң ң Ҥ ҥ Ҧ ҧ Ҩ ҩ Ҫ ҫ Ҭ ҭ Ү ү Ұ ұ Ҳ ҳ Ҵ ҵ Ҷ ҷ Ҹ ҹ Һ һ Ҽ ҽ Ҿ ҿ Ӏ Ӂ ӂ Ӄ ӄ Ӆ ӆ Ӈ ӈ Ӊ ӊ Ӌ ӌ Ӎ ӎ ӏ Ӑ ӑ Ӓ ӓ Ӕ ӕ Ӗ ӗ Ә ә Ӛ ӛ Ӝ ӝ Ӟ ӟ Ӡ ӡ Ӣ ӣ Ӥ ӥ Ӧ ӧ Ө ө Ӫ ӫ Ӭ ӭ Ӯ ӯ Ӱ ӱ Ӳ ӳ Ӵ ӵ Ӷ ӷ Ӹ ӹ Ӻ ӻ Ӽ ӽ Ӿ ӿ Ԁ ԁ Ԃ ԃ Ԅ ԅ Ԇ ԇ Ԉ ԉ Ԋ ԋ Ԍ ԍ Ԏ ԏ Ԑ ԑ Ԓ ԓ Ạ ạ Ả ả Ấ ấ Ầ ầ Ẩ ẩ Ẫ ẫ Ậ ậ Ắ ắ Ằ ằ Ẳ ẳ Ẵ ẵ Ặ ặ Ẹ ẹ Ẻ ẻ Ẽ ẽ Ế ế Ề ề Ể ể Ễ ễ Ệ ệ Ỉ ỉ Ị ị Ọ ọ Ỏ ỏ Ố ố Ồ ồ Ổ ổ Ỗ ỗ Ộ ộ Ớ ớ Ờ ờ Ở ở Ỡ ỡ Ợ ợ Ụ ụ Ủ ủ Ứ ứ Ừ ừ Ử ử Ữ ữ Ự ự Ỵ ỵ Ỷ ỷ Ỹ ỹ ₫ Ţ ţ ƀ Ɓ Ƃ ƃ Ƅ ƅ Ɔ Ƈ ƈ Ɖ Ɗ Ƌ ƌ ƍ Ǝ Ə Ɛ Ƒ Ɠ Ɣ ƕ Ɩ Ɨ Ƙ ƙ ƚ ƛ Ɯ Ɲ ƞ Ɵ Ƣ ƣ Ƥ ƥ Ʀ Ƨ ƨ Ʃ ƪ ƫ Ƭ ƭ Ʈ Ʊ Ʋ Ƴ ƴ Ƶ ƶ Ʒ Ƹ ƹ ƺ ƻ Ƽ ƽ ƾ ƿ ǀ ǁ ǂ ǃ DŽ Dž dž LJ Lj lj NJ Nj nj Ǎ ǎ Ǐ ǐ Ǒ ǒ Ǔ ǔ Ǖ ǖ Ǘ ǘ Ǚ ǚ Ǜ ǜ Ǟ ǟ Ǡ ǡ Ǣ ǣ Ǥ ǥ Ǧ ǧ Ǩ ǩ Ǫ ǫ Ǭ ǭ Ǯ ǯ DZ Dz dz Ǵ ǵ Ƕ Ƿ Ǹ ǹ Ȁ ȁ Ȃ ȃ Ȅ ȅ Ȇ ȇ Ȉ ȉ Ȋ ȋ Ȍ ȍ Ȏ ȏ Ȑ ȑ Ȓ ȓ Ȕ ȕ Ȗ ȗ Ȝ ȝ Ȟ ȟ Ƞ ȡ Ȣ ȣ Ȥ ȥ Ȧ ȧ Ȩ ȩ Ȫ ȫ Ȭ ȭ Ȯ ȯ Ȱ ȱ Ȳ ȳ ȴ ȵ ȶ ȸ ȹ Ⱥ Ȼ ȼ Ƚ Ⱦ ȿ ɀ Ɂ ɂ Ƀ Ʉ Ʌ Ɇ ɇ Ɉ ɉ Ɋ ɋ Ɍ ɍ Ɏ ɏ ɐ ɑ ɒ ɓ ɔ ɕ ɖ ɗ ɘ ə ɛ ɞ ɟ ɠ ɡ ɢ ɣ ɤ ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ ɭ ɮ ɯ ɰ ɱ ɲ ɳ ɴ ɵ ɶ ɷ ɸ ɹ ɺ ɻ ɼ ɽ ɾ ɿ ʀ ʁ ʂ ʃ ʄ ʅ ʆ ʇ ʈ ʉ ʊ ʋ ʌ ʍ ʎ ʏ ʐ ʑ ʒ ʓ ʔ ʕ ʖ ʗ ʘ ʙ ʚ ʛ ʜ ʝ ʞ ʟ ʠ ʡ ʢ ʣ ʤ ʥ ʦ ʧ ʨ ʩ ʪ ʫ ʬ ʭ ʮ ʯ ʰ ʱ ʲ ʳ ʴ ʵ ʶ ʷ ʸ ʹ ʺ ʻ ʽ ʾ ʿ ˀ ˁ ˂ ˃ ˄ ˅ ˈ ˊ ˋ ˌ ˍ ˎ ˏ ː ˑ ˒ ˓ ˔ ˕ ˖ ˗ ˟ ˠ ˡ ˢ ˣ ˤ ˥ ˦ ˧ ˨ ˩ ˪ ˫ ˬ ˭ ˮ ˯ ˰ ˱ ˲ ˴ ˵ ˶ ˷ ˸ ˹ ˺ ˻ ˼ ˽ ˾ ˿̉ ʹ ͵ ͺ ͻ ͼ ͽ ; ϐ ϓ ϔ ϕ ϗ Ϙ ϙ Ϛ ϛ Ϝ ϝ Ϟ ϟ Ϡ ϡ ϰ ϱ ϲ ϳ ϴ ϵ ϶ Ϸ ϸ Ϲ Ϻ ϻ ϼ Ͻ Ͼ Ͽ Ԛ ԛ Ԝ ԝ ᴀ ᴁ ᴂ ᴃ ᴄ ᴅ ᴆ ᴇ ᴈ ᴉ ᴊ ᴋ ᴌ ᴍ ᴎ ᴏ ᴐ ᴑ ᴒ ᴓ ᴔ ᴕ ᴖ ᴗ ᴘ ᴙ ᴚ ᴛ ᴜ ᴝ ᴞ ᴟ ᴠ ᴡ ᴢ ᴣ ᴤ ᴥ ᴦ ᴧ ᴨ ᴩ ᴪ ᴫ ᴬ ᴭ ᴮ ᴯ ᴰ ᴱ ᴲ ᴳ ᴴ ᴵ ᴶ ᴷ ᴸ ᴹ ᴺ ᴻ ᴼ ᴽ ᴾ ᴿ ᵀ ᵁ ᵂ ᵃ ᵄ ᵅ ᵆ ᵇ ᵈ ᵉ ᵊ ᵋ ᵌ ᵍ ᵎ ᵏ ᵐ ᵑ ᵒ ᵓ ᵔ ᵕ ᵖ ᵗ ᵘ ᵙ ᵚ ᵛ ᵜ ᵝ ᵞ ᵟ ᵠ ᵡ ᵢ ᵣ ᵤ ᵥ ᵦ ᵧ ᵨ ᵩ ᵪ ᵫ ᵬ ᵭ ᵮ ᵯ ᵰ ᵱ ᵲ ᵳ ᵴ ᵵ ᵶ ᵷ ᵸ ᵹ ᵺ ᵻ ᵼ ᵽ ᵾ ᵿ ᶀ ᶁ ᶂ ᶃ ᶄ ᶅ ᶆ ᶇ ᶈ ᶉ ᶊ ᶋ ᶌ ᶍ ᶎ ᶏ ᶐ ᶑ ᶒ ᶓ ᶔ ᶕ ᶖ ᶗ ᶘ ᶙ ᶚ ᶛ ᶜ ᶝ ᶞ ᶟ ᶠ ᶡ ᶢ ᶣ ᶤ ᶥ ᶦ ᶧ ᶨ ᶩ ᶪ ᶫ ᶬ ᶭ ᶮ ᶯ ᶰ ᶱ ᶲ ᶳ ᶴ ᶵ ᶶ ᶷ ᶸ ᶹ ᶺ ᶻ ᶼ ᶽ ᶾ ᶿ Ḃ ḃ Ḅ ḅ Ḇ ḇ Ḉ ḉ Ḋ ḋ Ḍ ḍ Ḏ ḏ Ḑ ḑ Ḓ ḓ Ḕ ḕ Ḗ ḗ Ḙ ḙ Ḛ ḛ Ḝ ḝ Ḟ ḟ Ḡ ḡ Ḣ ḣ Ḥ ḥ Ḧ ḧ Ḩ ḩ Ḫ ḫ Ḭ ḭ Ḯ ḯ Ḱ ḱ Ḳ ḳ Ḵ ḵ Ḷ ḷ Ḹ ḹ Ḻ ḻ Ḽ ḽ Ṁ ṁ Ṃ ṃ Ṅ ṅ Ṇ ṇ Ṉ ṉ Ṋ ṋ Ṍ ṍ Ṏ ṏ Ṑ ṑ Ṓ ṓ Ṕ ṕ Ṗ ṗ Ṙ ṙ Ṛ ṛ Ṝ ṝ Ṟ ṟ Ṡ ṡ Ṣ ṣ Ṥ ṥ Ṧ ṧ Ṩ ṩ Ṫ ṫ Ṭ ṭ Ṯ ṯ Ṱ ṱ Ṳ ṳ Ṵ ṵ Ṷ ṷ Ṹ ṹ Ṻ ṻ Ṽ ṽ Ṿ ṿ Ẇ ẇ Ẉ ẉ Ẋ ẋ Ẍ ẍ Ẏ ẏ Ẑ ẑ Ẓ ẓ Ẕ ẕ ẖ ẗ ẘ ẙ ẚ ẛ ἀ ἁ ἂ ἃ ἄ ἅ ἆ ἇ Ἀ Ἁ Ἂ Ἃ Ἄ Ἅ Ἆ Ἇ ἐ ἑ ἒ ἓ ἔ ἕ Ἐ Ἑ Ἒ Ἓ Ἔ Ἕ ἠ ἡ ἢ ἣ ἤ ἥ ἦ ἧ Ἠ Ἡ Ἢ Ἣ Ἤ Ἥ Ἦ Ἧ ἰ ἱ ἲ ἳ ἴ ἵ ἶ ἷ Ἰ Ἱ Ἲ Ἳ Ἴ Ἵ Ἶ Ἷ ὀ ὁ ὂ ὃ ὄ ὅ Ὀ Ὁ Ὂ Ὃ Ὄ Ὅ ὐ ὑ ὒ ὓ ὔ ὕ ὖ ὗ Ὑ Ὓ Ὕ Ὗ ὠ ὡ ὢ ὣ ὤ ὥ ὦ ὧ Ὠ Ὡ Ὢ Ὣ Ὤ Ὥ Ὦ Ὧ ὰ ά ὲ έ ὴ ή ὶ ί ὸ ό ὺ ύ ὼ ώ ᾀ ᾁ ᾂ ᾃ ᾄ ᾅ ᾆ ᾇ ᾈ ᾉ ᾊ ᾋ ᾌ ᾍ ᾎ ᾏ ᾐ ᾑ ᾒ ᾓ ᾔ ᾕ ᾖ ᾗ ᾘ ᾙ ᾚ ᾛ ᾜ ᾝ ᾞ ᾟ ᾠ ᾡ ᾢ ᾣ ᾤ ᾥ ᾦ ᾧ ᾨ ᾩ ᾪ ᾫ ᾬ ᾭ ᾮ ᾯ ᾰ ᾱ ᾲ ᾳ ᾴ ᾶ ᾷ Ᾰ Ᾱ Ὰ Ά ᾼ ᾽ ι ᾿ ῀ ῁ ῂ ῃ ῄ ῆ ῇ Ὲ Έ Ὴ Ή ῌ ῍ ῎ ῏ ῐ ῑ ῒ ΐ ῖ ῗ Ῐ Ῑ Ὶ Ί ῝ ῞ ῟ ῠ ῡ ῢ ΰ ῤ ῥ ῦ ῧ Ῠ Ῡ Ὺ Ύ Ῥ ῭ ΅ ` ῲ ῳ ῴ ῶ ῷ Ὸ Ό Ὼ Ώ ῼ ´ ῾ ‌ ‍ ‎ ‏ ‒ ‖ ‟ ‪ ‫ ‬ ‭ ‮   ‴ ‾ ⁞       ⁰ ⁶ ⁹ ₐ ₑ ₒ ₓ ₔ ₠ ₡ ₢ ₥ ₦ ₨ ₩ ₭ ₮ ₯ ₰ ₱ ₲ ₳ ₴ ₵ ₹⃰ ℗ ⅍ ⅎ ⅓ ⅔ ↄ Ⱡ ⱡ Ɫ Ᵽ Ɽ ⱥ ⱦ Ⱨ ⱨ Ⱪ ⱪ Ⱬ ⱬ Ɑ ⱱ Ⱳ ⱳ ⱴ Ⱶ ⱶ ⱷ ⸗ ꜗ ꜘ ꜙ ꜚ ꜛ ꜜ ꜝ ꜞ ꜟ ꜠ ꜡ ꞈ ꞉ ꞊ Ꞌ ꞌ Ԕ ԕ Ԗ ԗ Ԙ ԙ Ԟ ԟ Ԡ ԡ Ԣ ԣ Ԥ ԥ Ԧ ԧ ₺ Ỻ Ɱ ẞ Ɐ Ỽ Ỿ Ꜣ Ꜥ Ꜧ Ꜩ Ꜫ Ꜭ Ꜯ Ꜳ Ꜵ Ꜷ Ꜹ Ꜻ Ꜽ Ꜿ Ꝁ Ꝃ Ꝅ Ꝇ Ꝉ Ꝋ Ꝍ Ꝏ Ꝑ Ꝓ Ꝕ Ꝗ Ꝙ Ꝛ Ꝝ Ꝟ Ꝡ Ꝥ Ꝧ Ꝩ Ꝫ Ꝭ Ꝯ Ꝺ Ꝼ Ᵹ Ꝿ Ꞁ Ꞃ Ꞅ Ꞇ Ɥ Ꞑ Ꞓ Ꞡ Ꞣ Ꞥ Ꞧ Ꞩ Ɦ Ɜ Ɡ Ɬ Ʞ Ʇ Ʝ Ꭓ Ꞵ Ꞷ ⱻ ẟ ⱸ ɜ ɝ ǝ ɚ ı ȷ ỻ ẜ ẝ ⱺ ⱼ ⱹ ꜣ ꜥ ꜧ ꜩ ꜫ ꜭ ꜯ ꜰ ꜱ ꜳ ꜵ ꜷ ꜹ ꜻ ꜽ ꜿ ꝁ ꝃ ꝅ ꝇ ꝉ ꝋ ꝍ ꝏ ꝑ ꝓ ꝕ ꝗ ꝙ ꝛ ꝝ ꝟ ꝡ ꝥ ꝧ ꝩ ꝫ ꝭ ꝯ ꝱ ꝲ ꝳ ꝴ ꝵ ꝶ ꝷ ꝸ ꝺ ꝼ ꝿ ꞁ ꞃ ꞅ ꞇ ꞎ ꞑ ꞓ ꞡ ꞣ ꞥ ꞧ ꞩ ꞵ ꞷ ꟺ ỽ ỿ ff ffi ffl fi fl ſt st ⁱ ꞏ ꟷ ꟻ ꟼ ꟽ ꟾ ꟿ Ԩ Ԫ Ԭ Ԯ Ꙁ Ꙃ Ꙅ Ꙇ Ꙉ Ꙋ Ꙍ Ꙏ Ꙑ Ꙓ Ꙕ Ꙗ Ꙙ Ꙛ Ꙝ Ꙟ Ꙡ Ꙣ Ꙥ Ꙧ Ꙩ Ꙫ Ꙭ Ꚁ Ꚃ Ꚅ Ꚇ Ꚉ Ꚋ Ꚍ Ꚏ Ꚑ Ꚓ Ꚕ Ꚗ Ꚙ Ꚛ ԩ ԫ ԭ ԯ ᲀ ᲁ ᲂ ᲃ ᲄ ᲅ ᲆ ᲇ ᲈ ꙁ ꙃ ꙅ ꙇ ꙉ ꙋ ꙍ ꙏ ꙑ ꙓ ꙕ ꙗ ꙙ ꙛ ꙝ ꙟ ꙡ ꙣ ꙥ ꙧ ꙩ ꙫ ꙭ ꚁ ꚃ ꚅ ꚇ ꚉ ꚋ ꚍ ꚏ ꚑ ꚓ ꚕ ꚗ ꚙ ꚛ ꙮ ꙿ ꚜ ꚝ Ͱ Ͳ Ͷ Ϗ Ϳ ͱ ͳ ͷ ℭ ℌ ℑ ℜ ℨ Ꝣ ꝣ ℬ ℋ ℐ ℒ ℿ ℛ Ⅎ Å ℂ ℍ ℕ ℙ ℚ ℝ ℤ ⅅ ℾ ℇ K ℰ ℱ ℳ Ꞗ Ꞙ Ꞛ Ꞝ Ꞟ ⅆ ⅇ ⅈ ⅉ ℽ ℼ ℎ ℏ ℯ ℴ ℊ ꞔ ꞕ ꞗ ꞙ ꞛ ꞝ ꞟ ꬰ ꬱ ꬲ ꬳ ꬴ ꬵ ꬶ ꬷ ꬸ ꬹ ꬺ ꬻ ꬼ ꬽ ꬾ ꬿ ꭀ ꭁ ꭂ ꭃ ꭄ ꭅ ꭆ ꭇ ꭈ ꭉ ꭊ ꭋ ꭌ ꭍ ꭎ ꭏ ꭐ ꭑ ꭒ ꭓ ꭔ ꭕ ꭖ ꭗ ꭘ ꭙ ꭚ ꭤ ₕ ₖ ₗ ₘ ₙ ₚ ₛ ₜ ꝰ ꟸ ꟹ ꭜ ꭝ ꭞ ꭟ ⸯ ⅟ ↉ ⅕ ⅖ ⅗ ⅘ ⅙ ⅚ ⅐ ⅑ ⅒ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ⁂ ⁌ ⁍ ⁅ ⁆ ‸ ⁁ ⁀ ⁐ ⸶ ⸷ ⁜ ⸓ ⸖ ⸈ ⸔ ⸎ ⁉ ⸭ ⁙ ⁕ ⸐ ⁛ ⁘ ‧ ⁃ ⸒ ⸘ ⁔ ⸄ ⸜ ⸌ ⸂ ⸉ ⸠ ⁎ ․ ⸫ ⸙ ⸏ ⁇ ⁈ ⸴ ⸳ ⸇ ⸆ ⸋ ※ ⸑ ⁋ ⸮ ⁏ ⸁ ⸀ ⸅ ⸝ ⸍ ⸃ ⸊ ⸡ ⸰ ⸬ ⁓ ⁖ ⸞ ⸟ ⸛ ⁊ ⸹ ⁝ ⸲ ⸸ ⸵ ⁑ ‥ ⁚ ⸪ ‿ ‣ ⸼ ⸽ ⸾ ⸿ ⹁ ꙳ ⸕ ⸱ ₍ ₎ ⸤ ⸥ ⸢ ⸣ ⸨ ⸩ ⸦ ⸧ ⁽ ⁾ ⸚ ‐ ‑ ⸻ ⸺ ⹀ ‶ ‵ ⁗ ‷ ⹂ ꤮ ꙾   

⁡ ⁤ ⁣ ⁢ ⁦ ⁧ ⁨ ⁩ ⁠ ₶ ₼ ₽ ₪ ₷ ₸ ₻ ₾ ℧ ℵ ⅋ ℶ ⁒ ℸ ₌ ⁼ ℷ ₋ ⁻ ‱ ₊ ⁺ ⅃ ⅀ ⅁ ⅂ ⅄ ℺ ℀ ℁ ℆ ℃ ℄ ℻ ℉ ℹ ‽ ℔ ℥ ⅌ ℞ ⅊ ℟ ℈ ℠ ⅏ ℡ ℣ ℘ ˞ ꭛ ℩̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̈̇̀́̋̂̌̆̊̃̄̅̍̎̏̐̑̒̓̔̽̾̿̀́͆͊͋͌̕̚͏᷐᷎͓͔͕͖͙͚᷿᷂᪵᪶᪷᪸᪹᪺᪽᷽͐͑͒͗͛᷉᷇᷾᷋︦︤︥᷁᷀᷈᷅᷄᷌᷆᷃᪰᪱᪲᪳᪴᪻᪼ᷧᷨᷩᷪᷫᷬᷭᷮᷯᷰᷱᷲᷳᷴ᷵᷻͘͜͟͢᷼͝͞͠͡᷍ ⷠ ⷡ ⷢ ⷣ ⷤ ⷥ ⷦ ⷧ ⷨ ⷩ ⷪ ⷫ ⷬ ⷭ ⷮ ⷯ ⷰ ⷱ ⷲ ⷳ ⷴ ⷵ ⷶ ⷷ ⷸ ⷹ ⷺ ⷻ ⷼ ⷽ ⷾ ⷿ ︀︧︨︩︪︫︬︭᷏᷑᷒᪾͂̓̈́ͅ ҃ ҄ ҅ ҆ ҇ ꙯ ꙴ ꙵ ꙶ ꙷ ꙸ ꙹ ꙺ ꙻ ꙼ ꙽ ꚞ ꚟ︮︯ ꙰ ꙱ ꙲᷊ᷛᷞᷟᷡᷢͣᷔᷕᷖᷗͨͩͤᷙᷓᷚͪͥᷘᷜᷝᷥͫᷠͦͬᷣᷤͭͧͮͯᷦ ⱽ Ɒ Ȿ Ɀ ꭥ Ɪ ꭠ ꭡ ꭢ ꭣ ₿ ⹃ ⹄ ꜀ ꜁ ꜂ ꜃ ꜄ ꜅ ꜆ ꜇ ꜈ ꜉ ꜊ ꜋ ꜌ ꜍ ꜎ ꜏ ꜐ ꜑ ꜒ ꜓ ꜔ ꜕ ꜖ -------------------------------------------------------------------------------- /qtest/test-fancy-sauce-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-fancy-sauce-tester.pdf -------------------------------------------------------------------------------- /qtest/test-fancy-sauce.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require quadwriter) 3 | 4 | (define doc 5 | '(q 6 | (q "Brennan likes fancy sauce.") 7 | (para-break) 8 | (q "Dale hates fancy sauce."))) 9 | 10 | (provide doc) 11 | -------------------------------------------------------------------------------- /qtest/test-font-setup-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-font-setup-tester.pdf -------------------------------------------------------------------------------- /qtest/test-font-setup.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter 2 | 3 | '(q ((font-family "arbitrary-name")(font-size "24")) "This text is in Charter") 4 | '(para-break) 5 | '(q ((font-family "charter")(font-size "24")) "This text is in Charter too") 6 | -------------------------------------------------------------------------------- /qtest/test-font-tracking-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-font-tracking-tester.pdf -------------------------------------------------------------------------------- /qtest/test-font-tracking.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter 2 | 3 | '(q "Left aligned") 4 | '(para-break) 5 | 6 | '(q ((font-size "20")(font-tracking "5")) "we have the same tracking you see") 7 | '(para-break) 8 | '(q ((font-size "20")(font-tracking "0.25em")) "we have the same tracking you see") 9 | 10 | '(para-break) 11 | 12 | '(q "Justified") 13 | '(para-break) 14 | 15 | 16 | '(q ((font-size "20")(font-tracking "5")(line-align "justify")) "we have the same tracking you see") 17 | '(para-break) 18 | '(q ((font-size "20")(font-tracking "0.25em")(line-align "justify")) "we have the same tracking you see") 19 | -------------------------------------------------------------------------------- /qtest/test-hello-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-hello-tester.pdf -------------------------------------------------------------------------------- /qtest/test-hello.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter/markdown 2 | 3 | Hello world -------------------------------------------------------------------------------- /qtest/test-image-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-image-tester.pdf -------------------------------------------------------------------------------- /qtest/test-image.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter/markdown 2 | 3 | Here's a jpeg 4 | 5 | ![alt text](test.jpeg) 6 | 7 | And a png 8 | 9 | ![alt text](test.png) 10 | 11 | Done. -------------------------------------------------------------------------------- /qtest/test-kafka-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-kafka-tester.pdf -------------------------------------------------------------------------------- /qtest/test-keep-with-next-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-keep-with-next-tester.pdf -------------------------------------------------------------------------------- /qtest/test-keep-with-next.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter/markdown 2 | 3 | #:page-width "4in" 4 | #:page-height "2in" 5 | #:footer-display "none" 6 | 7 | Page 1 text many lines of the quad are kept together. 8 | 9 | ## heading sticks to p.2 10 | 11 | Page 2 text are kept together near a page break. -------------------------------------------------------------------------------- /qtest/test-metadata-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-metadata-tester.pdf -------------------------------------------------------------------------------- /qtest/test-metadata.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter 2 | 3 | #:pdf-title "My Life" 4 | #:pdf-author "Roxy Butterick" 5 | #:pdf-subject "pupography" 6 | #:pdf-keywords "cute boxer dog" 7 | #:footer-display "true" -------------------------------------------------------------------------------- /qtest/test-ot-features-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-ot-features-tester.pdf -------------------------------------------------------------------------------- /qtest/test-ot-features.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter 2 | 3 | 4 | '(q ((font-features "liga 0")) "No ligs: fifle") 5 | 6 | '(para-break) 7 | 8 | '(q ((font-features "liga 1")) "Ligs: fifle") 9 | 10 | '(para-break) 11 | 12 | '(q ((font-features "liga 1")) (q ((font-features "liga 0")) "No ligs: fifle")) 13 | 14 | '(para-break) 15 | 16 | '(q ((font-features "liga 1")) (q ((font-features "+ liga 0")) "No ligs: fifle")) 17 | 18 | '(para-break) 19 | 20 | '(q ((font-features "zero 1")) (q ((font-features "liga 0")) "No ligs, no slashed zero: fifle0")) 21 | 22 | '(para-break) 23 | 24 | '(q ((font-features "zero 1")) (q ((font-features "+ liga 0")) "No ligs, slashed zero: fifle0")) -------------------------------------------------------------------------------- /qtest/test-sections-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-sections-tester.pdf -------------------------------------------------------------------------------- /qtest/test-sections.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter 2 | 3 | '(q ((page-margin-gutter "1.5in")(page-margin-left "0.5in")(page-margin-right "0.5in")) 4 | 5 | "Section 1 Page 1 on right" 6 | 7 | (section-break) 8 | 9 | (q ((page-width "5in")(page-height "5in")(page-side-start "right")) "Section 2 Page 1 on right" 10 | 11 | (page-break) 12 | 13 | "Section 2 Page 2 on left") 14 | 15 | (section-break) 16 | 17 | (q ((page-width "5in")(page-height "5in")(page-side-start "left")) "Section 3 Page 1 on left" 18 | 19 | (page-break) 20 | 21 | "Section 3 Page 2 on right") 22 | 23 | ) -------------------------------------------------------------------------------- /qtest/test-symbol-tester.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test-symbol-tester.pdf -------------------------------------------------------------------------------- /qtest/test-symbol.rkt: -------------------------------------------------------------------------------- 1 | #lang quadwriter/markdown 2 | 3 | ``` 4 | excl-middl→¬¬-elim : ∀ {A : Set} → A ⊎ ¬ A → (¬ ¬ A → A) 5 | excl-middl→¬¬-elim (inj₁ a) = λ ¬¬a → a 6 | excl-middl→¬¬-elim (inj₂ ¬a) = λ ¬¬a → ⊥-elim (¬¬a ¬a) 7 | ``` 8 | -------------------------------------------------------------------------------- /qtest/test.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test.jpeg -------------------------------------------------------------------------------- /qtest/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/qtest/test.png -------------------------------------------------------------------------------- /qtest/update-tests.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require quadwriter "paths-to-test.rkt") 3 | 4 | (for-each update-test-pdf (find-test-paths)) 5 | -------------------------------------------------------------------------------- /quad/base.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require 4 | "quad.rkt" 5 | "position.rkt" 6 | "param.rkt" 7 | "util.rkt" 8 | "wrap.rkt") 9 | 10 | (provide (all-from-out "quad.rkt" 11 | "position.rkt" 12 | "param.rkt" 13 | "util.rkt" 14 | "wrap.rkt")) -------------------------------------------------------------------------------- /quad/checksum.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/match pkg/path) 3 | (provide pkg-checksum) 4 | 5 | (define (pkg-checksum [pkg "quad"] #:short [short? #false]) 6 | (match (for/or ([scope (in-list '(user installation shared))]) 7 | (hash-ref (read-pkgs-db scope) pkg #false)) 8 | [(pkg-info _ (? string? checksum) _) (if short? (substring checksum 0 7) checksum)] 9 | [_ "checksum not found"])) 10 | -------------------------------------------------------------------------------- /quad/doclang-raw.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require (for-syntax racket/base 4 | syntax/kerncase)) 5 | 6 | (provide (except-out (all-from-out racket/base) #%module-begin) 7 | (rename-out [*module-begin #%module-begin])) 8 | 9 | ;; Module wrapper ---------------------------------------- 10 | 11 | (define-syntax (*module-begin stx) 12 | (syntax-case stx () 13 | [(_ id post-process . body) 14 | (with-syntax ([exprs #'()]) 15 | #'(#%module-begin 16 | (doc-begin id post-process exprs . body)))])) 17 | 18 | (define-syntax (doc-begin stx) 19 | (syntax-case stx () 20 | [(_ m-id post-process (expr ...)) 21 | ;; unlike regular doclang, don't `provide` m-id (we'll do that in main-base wrapper) 22 | #`(define m-id (post-process (list . #,(reverse (syntax->list #'(expr ...))))))] 23 | [(_ m-id post-process exprs . body) 24 | ;; `body' probably starts with lots of string constants; it's 25 | ;; slow to trampoline on every string, so do them in a batch 26 | ;; here: 27 | (let loop ([body #'body] 28 | [accum null]) 29 | (syntax-case body () 30 | [(s . rest) 31 | (string? (syntax-e #'s)) 32 | (loop #'rest (cons #'s accum))] 33 | [() 34 | (with-syntax ([(accum ...) accum]) 35 | #`(doc-begin m-id post-process (accum ... . exprs)))] 36 | [(body1 . body) 37 | (with-syntax ([exprs (append accum #'exprs)]) 38 | (let ([expanded (local-expand 39 | #'body1 'module 40 | (append (kernel-form-identifier-list) 41 | (syntax->list #'(provide 42 | require 43 | #%provide 44 | #%require))))]) 45 | (syntax-case expanded (begin) 46 | [(begin body1 ...) 47 | #`(doc-begin m-id post-process exprs body1 ... . body)] 48 | [(id . rest) 49 | (and (identifier? #'id) 50 | (ormap (lambda (kw) (free-identifier=? #'id kw)) 51 | (syntax->list #'(require 52 | provide 53 | define-values 54 | define-syntaxes 55 | begin-for-syntax 56 | module 57 | module* 58 | #%require 59 | #%provide)))) 60 | #`(begin #,expanded (doc-begin m-id post-process exprs . body))] 61 | [_else 62 | #`(doc-begin m-id post-process 63 | ((pre-part #,expanded body1) . exprs) 64 | . body)])))]))])) 65 | 66 | (define-syntax (pre-part stx) 67 | (syntax-case stx () 68 | [(_ s e) 69 | (if (string? (syntax-e #'s)) 70 | #'s 71 | (with-syntax ([src (syntax-source #'e)] 72 | [line (syntax-line #'e)] 73 | [col (syntax-column #'e)] 74 | [pos (syntax-position #'e)] 75 | [span (syntax-column #'e)]) 76 | #'(check-pre-part e (vector 'src 'line 'col 'pos 'span))))])) 77 | 78 | (define (check-pre-part v s) 79 | v) -------------------------------------------------------------------------------- /quad/get-info.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require racket/class) 3 | (provide get-info) 4 | 5 | (define (custom-indent-rules x) 6 | (match x 7 | ;; tbd: custom indent rules, examples below 8 | #;[("with-pattern" 9 | "with-shared-id" 10 | "pattern-case" 11 | "pattern-case-filter") 'lambda] 12 | #;[("define-macro" 13 | "define-macro-cases" 14 | "define-cases" 15 | "while" 16 | "until") 'define] 17 | [_ #false])) 18 | 19 | (define (indenter t pos) 20 | (send t compute-racket-amount-to-indent pos custom-indent-rules)) 21 | 22 | (define (get-info key default-value proc) 23 | (define (fallback) (if proc (proc key default-value) default-value)) 24 | (define (try-dynamic-require lib export) 25 | (with-handlers ([exn:missing-module? 26 | (λ (x) (case key 27 | [(drracket:indentation) indenter] 28 | [else (fallback)]))]) 29 | (dynamic-require lib export))) 30 | (case key 31 | [(color-lexer) 32 | (try-dynamic-require 'syntax-color/scribble-lexer 'scribble-lexer)] 33 | [(drracket:indentation) 34 | (try-dynamic-require 'scribble/private/indentation 'determine-spaces)] 35 | [(drracket:keystrokes) 36 | (try-dynamic-require 'scribble/private/indentation 'keystrokes)] 37 | [else (fallback)])) -------------------------------------------------------------------------------- /quad/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | (define version "0.0") 3 | (define scribblings '(("scribblings/quad.scrbl" ()))) 4 | (define test-omit-paths 'all) -------------------------------------------------------------------------------- /quad/lang.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base) 3 | quad 4 | (prefix-in doclang: "doclang-raw.rkt")) 5 | (provide (rename-out [quad-mb #%module-begin]) 6 | #%top-interaction 7 | #%top 8 | #%app 9 | #%datum 10 | (except-out (all-from-out racket/base) #%module-begin)) 11 | 12 | (define-for-syntax export-name 'quad) 13 | (define-syntax (quad-mb stx) 14 | (syntax-case stx () 15 | [(_ . ARGS) 16 | (with-syntax ([EXPORT (datum->syntax stx export-name)]) 17 | #'(doclang:#%module-begin 18 | EXPORT ; positional arg for doclang-raw: name of export 19 | (λ (xs) 20 | (define x (q #:elems xs)) 21 | (println x) 22 | x) ; positional arg for doclang-raw: post-processor 23 | (provide EXPORT) 24 | (begin . ARGS)))])) -------------------------------------------------------------------------------- /quad/log.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/logging) 3 | (provide (all-defined-out)) 4 | 5 | ;; creates `quad-logger` and associated functions: 6 | ;; log-quad-fatal, log-quad-error, log-quad-warning, 7 | ;; log-quad-info, and log-quad-debug 8 | (define-logger quad) -------------------------------------------------------------------------------- /quad/main.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | ;; reader hook for #lang quad 4 | (module reader racket/base 5 | (require "reader.rkt") 6 | (provide (all-from-out "reader.rkt"))) 7 | 8 | (require "atomize.rkt" 9 | "quad.rkt" 10 | "qexpr.rkt" 11 | "wrap.rkt" 12 | "position.rkt" 13 | "param.rkt" 14 | "util.rkt" 15 | "checksum.rkt") 16 | 17 | (provide (all-from-out "atomize.rkt" 18 | "quad.rkt" 19 | "qexpr.rkt" 20 | "wrap.rkt" 21 | "position.rkt" 22 | "param.rkt" 23 | "util.rkt" 24 | "checksum.rkt")) -------------------------------------------------------------------------------- /quad/param.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide (all-defined-out)) 3 | 4 | (define current-default-attrs (make-parameter (make-hasheq))) 5 | (define current-wrap-distance (make-parameter 1)) 6 | (define current-default-font-size (make-parameter 12)) 7 | (define current-missing-glyph-action (make-parameter #f)) ; #f or 'error or 'warning or 'fallback or 'omit -------------------------------------------------------------------------------- /quad/pict.rkt: -------------------------------------------------------------------------------- 1 | #lang br 2 | (require quad pict) 3 | (provide (all-defined-out)) 4 | 5 | (define (quad->pict q) 6 | (match-define (list xmin ymin xmax ymax) (bounding-box q)) 7 | (define scaling-factor 3) 8 | (define stroke-width 0.5) 9 | (define margin 3) 10 | (unsafe-dc 11 | (λ (dc dx dy) 12 | (send dc scale scaling-factor scaling-factor) 13 | (send dc translate (+ (- xmin) stroke-width margin) (+ (- ymin) stroke-width margin)) 14 | (let loop ([q q][idx 0]) 15 | ;; outer edge 16 | (send dc set-pen "slategray" stroke-width 'solid) 17 | (send dc set-brush "white" 'solid) 18 | (define args (append (quad-origin q) (quad-size q))) 19 | (send dc draw-rectangle . args) 20 | ;; join pt 21 | (send dc set-pen "slategray" 0 'solid) 22 | (send dc set-brush (if (zero? idx) "black" "red") 'solid) 23 | (define pt-args (append (map sub1 (to-point q)) (list 2 2))) 24 | (send dc draw-rectangle . pt-args) 25 | (map (λ (qe) (loop qe (add1 idx))) (quad-elems q)))) 26 | (* scaling-factor (+ (- xmax xmin) (* stroke-width 2) (* margin 2))) 27 | (* scaling-factor (+ (- ymax ymin) (* stroke-width 2) (* margin 2))))) 28 | 29 | (module+ main 30 | (define q1 (make-quad #:size '(25 25))) 31 | (define q2 (make-quad #:size '(15 15))) 32 | (quad->pict (position (attach-to q1 'e q2 'w)))) -------------------------------------------------------------------------------- /quad/qexpr.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | (require xml 3 | racket/dict 4 | racket/string 5 | racket/match 6 | racket/list 7 | racket/path 8 | txexpr 9 | "quad.rkt") 10 | (provide (all-defined-out)) 11 | 12 | (module+ test (require rackunit)) 13 | 14 | ;; should we allow quads within a qexpr? I say yes 15 | (define permissive-qexprs (make-parameter #t)) 16 | 17 | (define (valid-tag? tag) (symbol? tag)) 18 | 19 | (define (qexpr? x) 20 | ;; a qexpr is like an xexpr, but more lenient in some ways (possibly allows quads) 21 | ;; attrs are open-ended 22 | (match x 23 | [(cons (? valid-tag?) rest) 24 | (match rest 25 | [(list (? txexpr-attrs?) (? qexpr?) ...) #t] 26 | [(list (? qexpr?) ...) #t] 27 | [_ #f])] 28 | [(? string?) #t] 29 | [(? quad?) (permissive-qexprs)] 30 | [_ #f])) 31 | 32 | 33 | (module+ test 34 | (check-true (qexpr? "Hello world")) 35 | (check-true (qexpr? '(q "Hello world"))) 36 | (check-true (qexpr? '(quad "Hello world"))) 37 | (check-true (qexpr? '(div "Hello world"))) 38 | (check-false (qexpr? '(q #\H))) 39 | (check-false (qexpr? '(quad #\H))) 40 | (check-false (qexpr? '(span #\H))) 41 | (check-true (qexpr? '(quad "Hello world"))) 42 | (check-true (qexpr? `(quad "Hello " ,(q "world"))))) 43 | 44 | (define (quad-qexpr-name q) (string->symbol (string-trim (symbol->string (object-name q)) "$"))) 45 | 46 | (define (qexpr #:clean-attrs? [clean-attrs? #f] 47 | #:name [name 'q] 48 | attrs . elems) 49 | (define new-attrs (if clean-attrs? (remove-duplicates attrs #:key car) attrs)) 50 | (define new-elems (match elems 51 | [(list (? char? c)) (list (string c))] 52 | [(list (? list? xs)) xs] 53 | [else elems])) 54 | (cond 55 | [(empty? new-attrs) (list* name new-elems)] 56 | [else (list* name new-attrs new-elems)])) 57 | 58 | (module+ test 59 | (check-equal? (qexpr null "foo") '(q "foo")) 60 | (check-equal? (qexpr '((k "v")) "foo") '(q ((k "v")) "foo")) 61 | (check-equal? (qexpr '((k "v2")(k "v1")) "foo") '(q ((k "v2")(k "v1")) "foo")) 62 | (check-equal? (qexpr #:clean-attrs? #t '((k "v2")(k "v1")) "foo") '(q ((k "v2")) "foo"))) 63 | 64 | (define (hash->qattrs attr-hash) 65 | (for/list ([(k v) (in-dict (hash->list attr-hash))]) 66 | (list k (format "~a" v)))) 67 | 68 | (define (quad->qexpr q) 69 | (let loop ([x q]) 70 | (cond 71 | [(quad? x) (apply qexpr #:name (quad-qexpr-name x) #:clean-attrs? #t (hash->qattrs (quad-attrs x)) (map loop (quad-elems x)))] 72 | [else x]))) 73 | 74 | (define (qexpr->quad x) 75 | (unless (qexpr? x) 76 | (raise-argument-error 'qexpr->quad "qexpr" x)) 77 | (let loop ([x x]) 78 | (match x 79 | [(cons (? valid-tag? tag) rest) 80 | (match rest 81 | [(list (? txexpr-attrs? attrs) (? qexpr? elems) ...) 82 | (define mheq (make-hasheq)) ; want mutable hash 83 | (for ([kv (in-list attrs)]) 84 | (match-define (list k v) kv) 85 | ;; coerce number strings to actual numbers 86 | ;; this misbehaves on a list index like "1." which becomes 1.0 87 | (hash-set! mheq k (cond 88 | [(equal? v "true") #true] 89 | [(equal? v "false") #false] 90 | [(string->number v)] 91 | [else v]))) 92 | (make-quad #:tag tag 93 | #:attrs mheq 94 | #:elems (map loop elems))] 95 | [(list (? qexpr? elems) ...) 96 | (make-quad #:tag tag 97 | #:elems (map loop elems))])] 98 | [_ x]))) 99 | 100 | (module+ test 101 | (check-true 102 | (quad=? 103 | (qexpr->quad `(q ((font "charter") (fontsize "12")) (q "Foo bar") ,(make-quad "zzz") (q "Zim Zam"))) 104 | (q (hasheq 'font "charter" 'fontsize 12) (q "Foo bar") (q "zzz") (q "Zim Zam"))))) 105 | 106 | (define qml-extension #".qml") 107 | (define (qml-path? x) 108 | (and (or (path-string? x) (path-for-some-system? x)) 109 | (for/or ([ext (in-list (list qml-extension 110 | (string->bytes/utf-8 111 | (string-upcase 112 | (bytes->string/utf-8 qml-extension)))))]) 113 | (path-has-extension? x ext)))) 114 | 115 | (module+ test 116 | (check-true (qml-path? "foo.qml")) 117 | (check-true (qml-path? "foo.QML")) 118 | (check-false (qml-path? "foo.QmL")) 119 | (check-false (qml-path? "foo.qmla"))) 120 | 121 | (define (qml->qexpr x) 122 | (parameterize ([permissive-xexprs #t] 123 | [xexpr-drop-empty-attributes #t]) 124 | (string->xexpr x))) 125 | 126 | (define (qexpr->qml x) 127 | (xexpr->string x)) 128 | 129 | (module+ test 130 | (check-equal? (qml->qexpr (qexpr->qml '(q "hi"))) '(q "hi")) 131 | (check-equal? (qml->qexpr (qexpr->qml '(q () "hi"))) '(q "hi")) 132 | (check-equal? (qml->qexpr (qexpr->qml '(q ((foo "bar")) "hi"))) '(q ((foo "bar")) "hi"))) -------------------------------------------------------------------------------- /quad/reader.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | (provide (rename-out 3 | [quad-lang-read read] 4 | [quad-lang-read-syntax read-syntax] 5 | [qr-mod:get-info get-info])) 6 | 7 | (module quad-reader syntax/module-reader 8 | #:language 'quad/lang 9 | #:info qgi:get-info 10 | #:read my-read 11 | #:read-syntax my-read-syntax 12 | #:whole-body-readers? #true 13 | (require (prefix-in qgi: quad/get-info) (prefix-in at: scribble/reader)) 14 | (define (my-read ip) (syntax->datum (my-read-syntax ip))) 15 | (define (my-read-syntax src ip) 16 | (define reader (at:make-at-reader 17 | #:command-char #\◊ 18 | #:syntax? #t 19 | #:inside? #t)) 20 | (reader src ip))) 21 | 22 | (require debug/reader (prefix-in qr-mod: 'quad-reader)) 23 | 24 | #| 25 | Use wrap-reader on the whole-module read function that would be exported 26 | by the reader module, not the single-expression read function like 27 | at:read-syntax that you deal with within syntax/module-reader or normal use. 28 | |# 29 | 30 | (define quad-lang-read (wrap-reader qr-mod:read)) 31 | (define quad-lang-read-syntax (wrap-reader qr-mod:read-syntax)) -------------------------------------------------------------------------------- /quad/rebase.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/list) 3 | (provide (all-defined-out)) 4 | 5 | (define (base-base digit-str 6 | #:offset [offset 0] 7 | #:min-width [min-width #f]) 8 | (define digits (list->vector (string->list digit-str))) 9 | (define len (vector-length digits)) 10 | (values 11 | (λ (num) 12 | (let loop ([num (+ num offset)] [acc null]) 13 | (if (zero? num) 14 | (list->string 15 | (if (and min-width (< (length acc) min-width)) 16 | (append (make-list (- min-width (length acc)) (vector-ref digits 0)) acc) 17 | acc)) 18 | (let* ([r (modulo num len)] 19 | [q (quotient (- num r) len)]) 20 | (loop q (cons (vector-ref digits r) acc)))))) 21 | (λ (str) 22 | (define digit-table (for/hash ([c (in-string digit-str)] 23 | [i (in-naturals)]) 24 | (values c i))) 25 | (- (for/sum ([c (in-list (reverse (string->list str)))] 26 | [i (in-naturals)]) 27 | (* (hash-ref digit-table c) (expt len i))) offset)))) 28 | 29 | (define-values (base62 unbase62) 30 | (base-base "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")) 31 | 32 | (define-values (base-readable unbase-readable) 33 | (base-base "abcdefghjkmpqrtuvwxy2346789")) 34 | 35 | (define-values (zbase32 unzbase32) 36 | (base-base "ybndrfg8ejkmcpqxot1uwisza345h769")) 37 | 38 | (define-values (rfc4648 unrfc4648) 39 | (base-base "abcdefghijklmnopqrstuvwxyz234567")) 40 | 41 | (define-values (no-vowel un-no-vowel) 42 | (base-base "bcdfghjklmnpqrstvwxz0123456789")) 43 | 44 | (define-values (base32-uc unbase32-uc) 45 | (base-base "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")) 46 | 47 | (define-values (base-uc unbase-uc) 48 | (base-base "ABCDEFGHIJKLMNOPQRSTUVWXYZ")) 49 | 50 | 51 | (module+ test 52 | (require rackunit) 53 | (check-equal? (base62 10234) "2F4") 54 | (check-equal? (unbase62 "2F4") 10234)) -------------------------------------------------------------------------------- /quad/scribblings/pollen-example/pollen.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require pollen/decode quadwriter) 3 | (provide root render-pdf) 4 | 5 | (define (root . xs) 6 | `(q ,@(add-between (decode-paragraphs xs 'q) para-break))) 7 | -------------------------------------------------------------------------------- /quad/scribblings/pollen-example/template.pdf.p: -------------------------------------------------------------------------------- 1 | ◊(render-pdf doc #false) -------------------------------------------------------------------------------- /quad/scribblings/pollen-example/test.pdf.pm: -------------------------------------------------------------------------------- 1 | #lang pollen 2 | 3 | Brennan likes fancy sauce. 4 | 5 | Dale hates fancy sauce. -------------------------------------------------------------------------------- /quad/scribblings/quads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quad/scribblings/quads.png -------------------------------------------------------------------------------- /quad/unicode/unicode-class-prep.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | (require (for-syntax racket/base) 3 | racket/match 4 | racket/port 5 | racket/string) 6 | 7 | (module+ reader 8 | (require syntax/strip-context) 9 | (provide (rename-out [rs read-syntax])) 10 | (define (rs name ip) 11 | (define src-lines (port->lines ip)) 12 | (define pred-id (string->symbol (string-trim (cadr src-lines)))) 13 | (define lines 14 | (for*/list ([line (in-list (cddr src-lines))] 15 | [str (in-value (string-trim (string-trim line #px"#.*" #:left? #false)))] 16 | #:when (non-empty-string? str)) 17 | (match-define (list* codepoints tag _) (string-split str ";")) 18 | ;; codepoints might be a single value, an x..y range, or two values (base value and modifier) 19 | ;; we want to ignore the modifier 20 | (define cp-or-range (match (string-split (string-trim codepoints) " ") 21 | [(list cp-or-range) cp-or-range] 22 | [(list cp modifiers ...) cp])) 23 | (list (map (λ (str) (string->number (string-trim str) 16)) (string-split cp-or-range "..")) 24 | (string->symbol (string-trim tag))))) 25 | (strip-context 26 | (with-syntax ([PRED-ID pred-id] 27 | [LINES lines]) 28 | #'(module _ quad/unicode/unicode-class-prep 29 | PRED-ID 30 | . LINES))))) 31 | 32 | (define-syntax (make-cond stx) 33 | (syntax-case stx () 34 | [(_ ID VAL) #'(eq? ID VAL)] ;; I believe `eq?` is OK because a codepoint is a fixnum 35 | [(_ ID LVAL RVAL) #'(<= LVAL ID RVAL)])) 36 | 37 | (provide (rename-out [mb #%module-begin]) 38 | (except-out (all-from-out racket/base) #%module-begin)) 39 | (define-syntax (mb stx) 40 | (syntax-case stx () 41 | [(_ PRED-ID (VALS RES) ...) 42 | 43 | #'(#%module-begin 44 | (provide PRED-ID) 45 | (define (PRED-ID x) 46 | (define cint (let loop ([x x]) 47 | (match x 48 | [(? char? c) (char->integer c)] 49 | [(? string? s) #:when (= 1 (string-length s)) 50 | (loop (car (string->list s)))] 51 | [(? integer?) x] 52 | [_ (raise-argument-error 'PRED-ID "integer, character, or one-character string" x)]))) 53 | (cond 54 | [(make-cond cint . VALS) 'RES] ... 55 | [else #f])))])) -------------------------------------------------------------------------------- /quad/util.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require racket/match racket/list) 3 | (provide (all-defined-out)) 4 | 5 | (define (contiguous-group-by pred xs [equality equal?]) 6 | ;; like `group-by`, but only groups together contiguous xs with the same pred value. 7 | (let loop ([xs xs][groups null]) 8 | (match xs 9 | [(== empty equality) (reverse groups)] 10 | [(cons first-x other-xs) 11 | (define equivalence-val (pred first-x)) 12 | (define-values (group-members rest) (splitf-at other-xs (λ (x) (equal? (pred x) equivalence-val)))) 13 | (define new-group (cons first-x group-members)) ; group-members might be empty 14 | (loop rest (cons new-group groups))]))) 15 | 16 | (module+ test 17 | (require rackunit) 18 | (check-equal? 19 | (contiguous-group-by values '(1 1 2 2 2 3 4 5 5 6 6 7 8 9)) 20 | '((1 1) (2 2 2) (3) (4) (5 5) (6 6) (7) (8) (9)))) -------------------------------------------------------------------------------- /quad2/main.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | (require racket/contract racket/function rackunit) 3 | 4 | (struct $point (x y) #:transparent #:mutable) 5 | (struct $size (width height) #:transparent #:mutable) 6 | (struct $rect (origin size) #:transparent #:mutable) 7 | 8 | (struct $quad (posn char) #:transparent #:mutable) 9 | 10 | (define/contract (posn-add p0 p1) 11 | ($point? $size? . -> . $point?) 12 | ($point (+ ($point-x p0) ($size-width p1)) (+ ($point-y p0) ($size-height p1)))) 13 | 14 | (define/contract (size char) 15 | ($quad? . -> . $size?) 16 | ($size 1 1)) 17 | 18 | (define/contract (advance char) 19 | ($quad? . -> . $size?) 20 | ($size 1 0)) 21 | 22 | (define pass/c ((listof $quad?) . -> . (listof $quad?))) 23 | 24 | (define/contract (quadify str) 25 | (string? . -> . (listof $quad?)) 26 | (for/list ([c (in-string str)]) 27 | ($quad #f c))) 28 | 29 | (define/contract (make-compiler . passes) 30 | (() #:rest (listof pass/c) 31 | . ->* . (string? . -> . (listof $quad?))) 32 | (apply compose1 (reverse (cons quadify passes)))) 33 | 34 | (define-syntax-rule (define-pass (PASS-NAME ARG) 35 | #:precondition PRECOND-PROC 36 | #:postcondition POSTCOND-PROC 37 | EXPRS ...) 38 | (define/contract (PASS-NAME ARG) 39 | pass/c 40 | (unless (PRECOND-PROC ARG) 41 | (error 'precondition-failed)) 42 | (define res (let () EXPRS ...)) 43 | (unless (POSTCOND-PROC res) 44 | (error 'postcondition-failed)) 45 | res)) 46 | 47 | (define (min-x rect) ($point-x ($rect-origin rect))) 48 | (define (width rect) ($size-width ($rect-size rect))) 49 | (define (max-x rect) (+ (min-x rect) (width rect))) 50 | (define (min-y rect) ($point-y ($rect-origin rect))) 51 | (define (height rect) ($size-height ($rect-size rect))) 52 | (define (max-y rect) (+ (min-y rect) (height rect))) 53 | 54 | (define/contract (rect-contains-point? rect pt) 55 | ($rect? $point? . -> . boolean?) 56 | (and (<= (min-x rect) ($point-x pt) (max-x rect)) 57 | (<= (min-y rect) ($point-y pt) (max-y rect)))) 58 | 59 | (define/contract (rect-contains-rect? outer inner) 60 | ($rect? $rect? . -> . boolean?) 61 | (and (rect-contains-point? outer ($rect-origin inner)) 62 | (rect-contains-point? outer ($point (max-x inner) (max-y inner))))) 63 | 64 | (define (has-position? q) (not (eq? ($quad-posn q) #false))) 65 | (define-pass (layout qs) 66 | #:precondition (λ (qs) (andmap (λ (q) (not (has-position? q))) qs)) 67 | #:postcondition (λ (qs) (andmap has-position? qs)) 68 | (define frame ($rect ($point 0 0) ($size 5 30))) 69 | (define (quad-fits? q posn) 70 | (define q-size (size q)) 71 | (define quad-rect ($rect posn q-size)) 72 | (and (rect-contains-rect? frame quad-rect) posn)) 73 | (for/fold ([posn ($point 0 0)] 74 | #:result qs) 75 | ([q (in-list qs)]) 76 | (define first-posn-on-next-line ($point 0 (add1 ($point-y posn)))) 77 | (define winning-posn (or (ormap (λ (posn) (quad-fits? q posn)) (list posn first-posn-on-next-line)) (error 'no-posn-that-fits))) 78 | (set-$quad-posn! q winning-posn) 79 | (posn-add winning-posn (advance q)))) 80 | 81 | (define compile (make-compiler layout)) 82 | (check-equal? (compile "Hello world") 83 | (list 84 | ($quad ($point 0 0) #\H) 85 | ($quad ($point 1 0) #\e) 86 | ($quad ($point 2 0) #\l) 87 | ($quad ($point 3 0) #\l) 88 | ($quad ($point 4 0) #\o) 89 | ($quad ($point 0 1) #\space) 90 | ($quad ($point 1 1) #\w) 91 | ($quad ($point 2 1) #\o) 92 | ($quad ($point 3 1) #\r) 93 | ($quad ($point 4 1) #\l) 94 | ($quad ($point 0 2) #\d))) -------------------------------------------------------------------------------- /quadwriter/block.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require "attrs.rkt" 3 | "param.rkt" 4 | "log.rkt" 5 | "debug.rkt" 6 | "struct.rkt" 7 | quad/base 8 | pitfall) 9 | (provide (all-defined-out)) 10 | 11 | 12 | (define ((block-draw-start first-line) q doc) 13 | ;; adjust drawing coordinates for border inset 14 | (match-define (list bil bit bir bib) 15 | (for/list ([k (in-list (list :border-inset-left :border-inset-top :border-inset-right :border-inset-bottom))]) 16 | (quad-ref first-line k 0))) 17 | (match-define (list left top) (pt+ (quad-origin q) (list bil bit))) 18 | (match-define (list width height) (pt- (size q) (list (+ bil bir) (+ bit bib)))) 19 | ;; fill rect 20 | (let ([bgc (quad-ref first-line :background-color)]) 21 | (when bgc 22 | (rect doc left top width height) 23 | (fill doc bgc))) 24 | ;; draw border 25 | (match-define (list bw-left bw-top bw-right bw-bottom) 26 | (map (λ (k) (max 0 (quad-ref first-line k 0))) 27 | (list 28 | :border-width-left 29 | :border-width-top 30 | :border-width-right 31 | :border-width-bottom))) 32 | ;; adjust start and end points based on adjacent border width 33 | ;; so all borders overlap rectangularly 34 | (define (half x) (/ x 2.0)) 35 | (define right (+ left width)) 36 | (define bottom (+ top height)) 37 | (define (box-side x1 y1 x2 y2 color stroke-width) 38 | (when (positive? stroke-width) 39 | (move-to doc x1 y1) 40 | (line-to doc x2 y2) 41 | (stroke doc (or color "black") stroke-width))) 42 | (box-side (- left (half bw-left)) top (+ right (half bw-right)) top 43 | (quad-ref first-line :border-color-top) bw-top) 44 | (box-side right (- top (half bw-top)) right (+ bottom (half bw-bottom)) 45 | (quad-ref first-line :border-color-right) bw-right) 46 | (box-side (+ right (half bw-right)) bottom (- left (half bw-left)) bottom 47 | (quad-ref first-line :border-color-bottom) bw-bottom) 48 | (box-side left (+ bottom (half bw-bottom)) left (- top (half bw-top)) 49 | (quad-ref first-line :border-color-left) bw-left) 50 | (case (quad-ref first-line :block-clip) 51 | [(#true) 52 | (when (eq? (log-clipping?) 'warn) 53 | (for ([line (in-list (quad-elems q))]) 54 | (define line-width (pt-x (size line))) 55 | (define line-elem-width (sum-x (quad-elems line))) 56 | (when (< line-width line-elem-width) 57 | (define error-str (apply string-append (for/list ([q (in-list (quad-elems line))]) 58 | (match (quad-elems q) 59 | [(list (? string? str)) str] 60 | [_ ""])))) 61 | (log-quadwriter-warning (format "clipping overfull line: ~v" error-str))))) 62 | (save doc) 63 | (rect doc left top width height) 64 | (clip doc)])) 65 | 66 | (define ((block-draw-end first-line) q doc) 67 | (case (quad-ref first-line :block-clip) 68 | [(#true) (restore doc)]) 69 | (when (draw-debug-block?) 70 | (draw-debug q doc "#6c6" "#9c9"))) 71 | 72 | (define (insert-blocks lines) 73 | (define groups-of-lines (contiguous-group-by (λ (x) (quad-ref x :display)) lines)) 74 | (append* (for/list ([line-group (in-list groups-of-lines)]) 75 | (if (quad-ref (car line-group) :display) 76 | (list (lines->block line-group)) 77 | line-group)))) 78 | 79 | (define (lines->block lines) 80 | (match lines 81 | [(cons line _) 82 | (make-quad 83 | #:type block-quad 84 | #:from 'sw 85 | #:to 'nw 86 | #:elems (from-parent lines 'nw) 87 | #:tag 'block 88 | #:attrs (quad-attrs line) 89 | #:size (delay (pt (pt-x (size line)) ; 90 | (+ (sum-y lines) 91 | (quad-ref line :inset-top 0) 92 | (quad-ref line :inset-bottom 0)))) 93 | #:shift-elems (pt 0 (quad-ref line :inset-top 0)) 94 | #:draw-start (block-draw-start line) 95 | #:draw-end (block-draw-end line))])) -------------------------------------------------------------------------------- /quadwriter/break.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require "struct.rkt" 3 | "attrs.rkt" 4 | quad/quad) 5 | (provide (all-defined-out)) 6 | 7 | 8 | (define (convert-break-quad q) 9 | ;; this is verbose & ugly because `struct-copy` is a macro 10 | ;; we want to use break prototypes but also preserve their type 11 | (match (quad-tag q) 12 | [(== 'para-break eq?) 13 | (quad-copy para-break-quad q:para-break [attrs (quad-attrs q)])] 14 | [(== 'line-break eq?) 15 | (quad-copy line-break-quad q:line-break [attrs (quad-attrs q)])] 16 | [(== 'page-break eq?) 17 | (quad-copy page-break-quad q:page-break [attrs (quad-attrs q)])] 18 | [(== 'column-break eq?) 19 | (quad-copy column-break-quad q:column-break [attrs (quad-attrs q)])] 20 | [(== 'hr eq?) (quad-copy hr-break-quad q:hr-break [attrs (quad-attrs q)])] 21 | [(== 'section-break eq?) 22 | (quad-copy section-break-quad q:section-break [attrs (quad-attrs q)])] 23 | [_ #false])) 24 | 25 | 26 | (module+ test 27 | (require rackunit quad/qexpr) 28 | (check-equal? (quad-ref (convert-break-quad (qexpr->quad '(page-break ((foo "bar"))))) 'foo) "bar")) 29 | 30 | 31 | (define q:line-break (make-line-break-quad #:printable #f 32 | #:tag 'line-break)) 33 | (define q:para-break (make-para-break-quad #:printable #f 34 | #:tag 'para-break)) 35 | (define q:hr-break (make-hr-break-quad #:printable #t 36 | #:tag 'hr-break)) 37 | (define q:column-break (make-column-break-quad #:printable #f 38 | #:tag 'column-break)) 39 | 40 | (define q:page-break (make-page-break-quad #:printable #f 41 | #:tag 'page-break)) 42 | 43 | (define q:section-break (make-section-break-quad #:printable #f 44 | #:tag 'section-break)) 45 | 46 | (define para-break '(para-break)) 47 | (define line-break '(line-break)) 48 | (define page-break '(page-break)) 49 | (define column-break '(column-break)) 50 | (define hr-break '(hr)) 51 | (define section-break '(section-break)) 52 | 53 | (module+ test 54 | (require rackunit quad/atomize) 55 | (check-true (line-break-quad? (second (quad-elems (q "foo" q:page-break "bar"))))) 56 | (check-true (line-break-quad? (second (atomize (q "foo" q:page-break "bar")))))) 57 | -------------------------------------------------------------------------------- /quadwriter/column.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require "attrs.rkt" 3 | "struct.rkt" 4 | "block.rkt" 5 | quad/base) 6 | (provide (all-defined-out)) 7 | 8 | (define q:column (make-quad 9 | #:type column-quad 10 | #:tag 'col 11 | #:from 'ne 12 | #:to 'nw)) 13 | 14 | (define q:column-spacer (make-quad 15 | #:type column-spacer-quad 16 | #:from 'ne 17 | #:to 'nw 18 | #:printable only-prints-in-middle)) 19 | 20 | (define ((column-wrap-finish col-quad) lns q0 ending-q idx [reversed-fn-lines null]) 21 | (define fn-lines 22 | (from-parent (for/list ([fn-line (in-list reversed-fn-lines)]) 23 | ;; position bottom to top, in reverse 24 | (quad-update! fn-line 25 | [from 'nw] 26 | [to 'sw])) 'sw)) 27 | 28 | (append 29 | (match lns 30 | [(cons line _) 31 | (list (quad-copy column-quad col-quad 32 | ;; move block attrs up, so they are visible in page wrap 33 | [attrs (copy-block-attrs (quad-attrs line) 34 | (hash-copy (quad-attrs col-quad)))] 35 | [elems (append (from-parent (insert-blocks lns) 'nw) fn-lines)]))] 36 | [_ null]) 37 | (match ending-q 38 | [(? page-break-quad? page-break) (list page-break)] ; hard page (or section) break 39 | [_ null]))) 40 | 41 | #| 42 | constraint wrapping example 43 | https://github.com/mbutterick/typesetter/blob/882ec681ad1fa6eaee6287e53bc4320d9656046b/pdf/directory-require.rkt#L51 44 | |# 45 | ;; 46 | 47 | 48 | (define (footnote-start? fnq) (quad-ref fnq :fn-text-start)) 49 | 50 | (define (handle-leftover-footnote ymax leftover-qs fn-qs) 51 | (let loop ([ymax ymax][leftover-qs leftover-qs][fn-qs fn-qs]) 52 | (define ydist (and (pair? fn-qs) (pt-y (size (car fn-qs))))) 53 | ;; take all fn lines that are not footnote-start? 54 | ;; and that fit within ymax remaining 55 | (if (and ydist (not (footnote-start? (car fn-qs))) (<= ydist ymax)) 56 | (loop (- ymax ydist) (cons (car fn-qs) leftover-qs) (cdr fn-qs)) 57 | (values ymax leftover-qs fn-qs)))) 58 | 59 | (define (handle-new-footnote ymax leftover-qs fn-qs fn-ref-q) 60 | (define ydist-fn (and (pair? fn-qs) 61 | (footnote-start? (car fn-qs)) 62 | (pt-y (size (car fn-qs))))) 63 | (define ydist-ref (pt-y (size fn-ref-q))) 64 | ;; only accept the footnote if both the first line of footnote 65 | ;; and the line containing the ref will fit. 66 | (if (and ydist-fn (<= (+ ydist-fn ydist-ref) ymax)) 67 | (values (- ymax ydist-fn) (cons (car fn-qs) leftover-qs) (cdr fn-qs)) 68 | (raise 'boom))) 69 | 70 | (define (column-wrap lines fn-lines vertical-height column-gap [col-quad-proto q:column]) 71 | (unless (positive? vertical-height) 72 | (raise-argument-error 'column-wrap "positive number" vertical-height)) 73 | 74 | ;; on timing of `insert-blocks`: 75 | ;; can't do it before because it depends on where columns are broken. 76 | ;; could do it after, but it would require going back inside each col quad 77 | ;; which seems overly interdependent, because `insert-blocks` is used to determine break locations. 78 | ;; `col-wrap` should emit quads that are complete. 79 | (define cols (wrap lines vertical-height 80 | #:soft-break #true 81 | #:hard-break column-break-quad? 82 | #:no-break (λ (q) (quad-ref q :no-colbr)) ; cooperates with make-nobreak 83 | #:distance (λ (q dist-so-far wrap-qs) 84 | ;; do trial block insertions 85 | (sum-y (insert-blocks (reverse wrap-qs)))) 86 | #:finish-wrap (column-wrap-finish col-quad-proto) 87 | #:footnote-qs fn-lines 88 | #:footnote-leftover-proc handle-leftover-footnote 89 | #:footnote-new-proc handle-new-footnote)) 90 | (define reversed-fn-lines 91 | (from-parent (for/list ([fn-line (in-list (reverse fn-lines))]) 92 | ;; position bottom to top, in reverse 93 | (quad-update! fn-line 94 | [from 'nw] 95 | [to 'sw])) 'sw)) 96 | (when (pair? cols) 97 | (quad-update! (car cols) 98 | [elems (append (quad-elems (car cols)) reversed-fn-lines)])) 99 | (define col-spacer (quad-copy column-spacer-quad q:column-spacer 100 | [size (pt column-gap (and 'arbitrary-irrelevant-value 100))])) 101 | (add-between cols col-spacer)) 102 | -------------------------------------------------------------------------------- /quadwriter/core.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | (require "render.rkt" 3 | "param.rkt" 4 | "break.rkt") 5 | (provide render-pdf (all-from-out "param.rkt" "break.rkt")) 6 | -------------------------------------------------------------------------------- /quadwriter/debug.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require pitfall 3 | quad/position 4 | quad/quad 5 | "attrs.rkt") 6 | (provide (all-defined-out)) 7 | 8 | (define-for-syntax debug-mode #false) 9 | 10 | (define-syntax (go stx) 11 | (datum->syntax stx 12 | (cond 13 | [debug-mode 14 | '(begin 15 | (define draw-debug? (make-parameter #true)) 16 | (define draw-debug-line? (make-parameter #true)) 17 | (define draw-debug-block? (make-parameter #false)) 18 | (define draw-debug-string? (make-parameter #true)) 19 | (define draw-debug-image? (make-parameter #false)) 20 | (define draw-debug-draw? (make-parameter #false)) 21 | 22 | (define debug-page-width (make-parameter 400)) 23 | (define debug-page-height (make-parameter 400)) 24 | (define debug-x-margin (make-parameter 50)) 25 | (define debug-y-margin (make-parameter 50)) 26 | (define debug-column-count (make-parameter 1)) 27 | (define debug-column-gap (make-parameter 36)))] 28 | [else 29 | '(begin 30 | (define draw-debug? (make-parameter #false)) 31 | (define draw-debug-line? (make-parameter #true)) 32 | (define draw-debug-block? (make-parameter #true)) 33 | (define draw-debug-string? (make-parameter #true)) 34 | (define draw-debug-image? (make-parameter #true)) 35 | (define draw-debug-draw? (make-parameter #true)) 36 | 37 | (define debug-page-width (make-parameter #f)) 38 | (define debug-page-height (make-parameter #f)) 39 | (define debug-x-margin (make-parameter #f)) 40 | (define debug-y-margin (make-parameter #f)) 41 | (define debug-column-count (make-parameter #f)) 42 | (define debug-column-gap (make-parameter #f)))]))) 43 | 44 | 45 | (go) 46 | 47 | 48 | (define (draw-debug q doc [fill-color "#f99"] [stroke-color "#fcc"] . _) 49 | (define stroke-width 0.5) 50 | (when (or (draw-debug?) (quad-ref q :draw-debug)) 51 | (save doc) 52 | ;; draw layout box 53 | (line-width doc stroke-width) 54 | ; subtracting stroke-width keeps adjacent boxes from overlapping 55 | (save doc) 56 | (apply rect doc (append (pt+ (quad-origin q)) (map (λ (x) (- x stroke-width)) (size q)))) 57 | (clip doc) 58 | (define pt (to-point q)) 59 | (circle doc (pt-x pt) (pt-y pt) (+ 3 stroke-width)) 60 | (fill doc fill-color) 61 | (restore doc) 62 | (apply rect doc (append (pt+ (quad-origin q)) (map (λ (x) (- x stroke-width)) (size q)))) 63 | (stroke doc stroke-color) 64 | (restore doc))) 65 | -------------------------------------------------------------------------------- /quadwriter/doc.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require "struct.rkt" 3 | quad/quad 4 | pitfall) 5 | (provide (all-defined-out)) 6 | 7 | (define q:doc (make-quad 8 | #:type doc-quad 9 | #:draw-start (λ (q doc) (start-doc doc)) 10 | #:draw-end (λ (q doc) (end-doc doc)))) 11 | -------------------------------------------------------------------------------- /quadwriter/draw.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require "struct.rkt" 3 | "string.rkt" 4 | "debug.rkt" 5 | "attrs.rkt" 6 | quad/quad 7 | sugar/coerce 8 | quad/position 9 | pitfall) 10 | (provide (all-defined-out)) 11 | 12 | 13 | (define q:draw (make-quad #:type draw-quad)) 14 | 15 | 16 | (define (draw-line q doc) 17 | (define x1 (quad-ref q :x1 0)) 18 | (define y1 (quad-ref q :y1 0)) 19 | (move-to doc x1 y1) 20 | (line-to doc (quad-ref q :x2 x1) (quad-ref q :y2 y1)) 21 | (line-width doc (quad-ref q :stroke 1)) 22 | (stroke doc (quad-ref q :color "black"))) 23 | 24 | (define (draw-text q doc) 25 | (move-to doc 0 0) 26 | (q:string-draw q doc 27 | #:origin (pt (quad-ref q :x 0) (quad-ref q :y 0)) 28 | #:text (quad-ref q :string ""))) 29 | 30 | 31 | (define (convert-draw-quad q) 32 | (cond 33 | [(memq (quad-tag q) '(line text)) 34 | (quad-copy draw-quad q:draw 35 | [from (->symbol (quad-ref q :anchor-from (quad-from q:draw)))] 36 | [from-parent (match (quad-ref q :anchor-from-parent (quad-from-parent q:draw)) 37 | [#false #false] 38 | [str (->symbol str)])] 39 | [to (->symbol (quad-ref q :anchor-to (quad-to q:draw)))] 40 | [elems (quad-elems q)] 41 | [tag (quad-tag q)] 42 | [attrs (quad-attrs q)] 43 | [size (match (quad-tag q) 44 | [(== 'text eq?) (make-size-promise-for-string q (quad-ref q :string ""))] 45 | [(== 'line eq?) (pt (abs (- (quad-ref q :x1) (quad-ref q :x2))) 46 | (abs (- (quad-ref q :y1) (quad-ref q :y2))))] 47 | [_ (pt (quad-ref q :width 0) (quad-ref q :height 0))])] 48 | [draw-end (λ (q doc) 49 | (when (draw-debug-draw?) 50 | (draw-debug q doc "red" "red")))] 51 | [draw (let ([draw-proc (match (quad-tag q) 52 | [(== 'line eq?) draw-line] 53 | [(== 'text eq?) draw-text])]) 54 | (λ (q doc) 55 | (save doc) 56 | (apply translate doc (quad-origin q)) 57 | (draw-proc q doc) 58 | (restore doc)))])] 59 | [else #false])) -------------------------------------------------------------------------------- /quadwriter/fonts/blockquote/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 2 | with Reserved Font Name Fira Sans. 3 | 4 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 5 | with Reserved Font Name Fira Mono. 6 | 7 | Copyright (c) 2014, Telefonica S.A. 8 | 9 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 10 | This license is copied below, and is also available with a FAQ at: 11 | http://scripts.sil.org/OFL 12 | 13 | 14 | ----------------------------------------------------------- 15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 16 | ----------------------------------------------------------- 17 | 18 | PREAMBLE 19 | The goals of the Open Font License (OFL) are to stimulate worldwide 20 | development of collaborative font projects, to support the font creation 21 | efforts of academic and linguistic communities, and to provide a free and 22 | open framework in which fonts may be shared and improved in partnership 23 | with others. 24 | 25 | The OFL allows the licensed fonts to be used, studied, modified and 26 | redistributed freely as long as they are not sold by themselves. The 27 | fonts, including any derivative works, can be bundled, embedded, 28 | redistributed and/or sold with any software provided that any reserved 29 | names are not used by derivative works. The fonts and derivatives, 30 | however, cannot be released under any other type of license. The 31 | requirement for fonts to remain under this license does not apply 32 | to any document created using the fonts or their derivatives. 33 | 34 | DEFINITIONS 35 | "Font Software" refers to the set of files released by the Copyright 36 | Holder(s) under this license and clearly marked as such. This may 37 | include source files, build scripts and documentation. 38 | 39 | "Reserved Font Name" refers to any names specified as such after the 40 | copyright statement(s). 41 | 42 | "Original Version" refers to the collection of Font Software components as 43 | distributed by the Copyright Holder(s). 44 | 45 | "Modified Version" refers to any derivative made by adding to, deleting, 46 | or substituting -- in part or in whole -- any of the components of the 47 | Original Version, by changing formats or by porting the Font Software to a 48 | new environment. 49 | 50 | "Author" refers to any designer, engineer, programmer, technical 51 | writer or other person who contributed to the Font Software. 52 | 53 | PERMISSION & CONDITIONS 54 | Permission is hereby granted, free of charge, to any person obtaining 55 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 56 | redistribute, and sell modified and unmodified copies of the Font 57 | Software, subject to the following conditions: 58 | 59 | 1) Neither the Font Software nor any of its individual components, 60 | in Original or Modified Versions, may be sold by itself. 61 | 62 | 2) Original or Modified Versions of the Font Software may be bundled, 63 | redistributed and/or sold with any software, provided that each copy 64 | contains the above copyright notice and this license. These can be 65 | included either as stand-alone text files, human-readable headers or 66 | in the appropriate machine-readable metadata fields within text or 67 | binary files as long as those fields can be easily viewed by the user. 68 | 69 | 3) No Modified Version of the Font Software may use the Reserved Font 70 | Name(s) unless explicit written permission is granted by the corresponding 71 | Copyright Holder. This restriction only applies to the primary font name as 72 | presented to the users. 73 | 74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 75 | Software shall not be used to promote, endorse or advertise any 76 | Modified Version, except to acknowledge the contribution(s) of the 77 | Copyright Holder(s) and the Author(s) or with their explicit written 78 | permission. 79 | 80 | 5) The Font Software, modified or unmodified, in part or in whole, 81 | must be distributed entirely under this license, and must not be 82 | distributed under any other license. The requirement for fonts to 83 | remain under this license does not apply to any document created 84 | using the Font Software. 85 | 86 | TERMINATION 87 | This license becomes null and void if any of the above conditions are 88 | not met. 89 | 90 | DISCLAIMER 91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 99 | OTHER DEALINGS IN THE FONT SOFTWARE. 100 | -------------------------------------------------------------------------------- /quadwriter/fonts/blockquote/bold-italic/fira-sans-bold-italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/blockquote/bold-italic/fira-sans-bold-italic.otf -------------------------------------------------------------------------------- /quadwriter/fonts/blockquote/bold/fira-sans-bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/blockquote/bold/fira-sans-bold.otf -------------------------------------------------------------------------------- /quadwriter/fonts/blockquote/italic/fira-sans-italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/blockquote/italic/fira-sans-italic.otf -------------------------------------------------------------------------------- /quadwriter/fonts/blockquote/regular/fira-sans.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/blockquote/regular/fira-sans.otf -------------------------------------------------------------------------------- /quadwriter/fonts/code/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. 2 | with Reserved Font Name < Fira >, 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /quadwriter/fonts/code/fira-mono.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/code/fira-mono.otf -------------------------------------------------------------------------------- /quadwriter/fonts/default/SourceSerifPro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/default/SourceSerifPro-Regular.otf -------------------------------------------------------------------------------- /quadwriter/fonts/fallback-emoji/LICENSE_OFL.txt: -------------------------------------------------------------------------------- 1 | This Font Software is licensed under the SIL Open Font License, 2 | Version 1.1. 3 | 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font 14 | creation efforts of academic and linguistic communities, and to 15 | provide a free and open framework in which fonts may be shared and 16 | improved in partnership with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply to 25 | any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software 36 | components as distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, 39 | deleting, or substituting -- in part or in whole -- any of the 40 | components of the Original Version, by changing formats or by porting 41 | the Font Software to a new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, 49 | modify, redistribute, and sell modified and unmodified copies of the 50 | Font Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, in 53 | Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the 64 | corresponding Copyright Holder. This restriction only applies to the 65 | primary font name as presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created using 77 | the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /quadwriter/fonts/fallback-emoji/NotoEmoji-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/fallback-emoji/NotoEmoji-Regular.ttf -------------------------------------------------------------------------------- /quadwriter/fonts/fallback-emoji/README: -------------------------------------------------------------------------------- 1 | This package is part of the noto project. Visit 2 | google.com/get/noto for more information. 3 | 4 | Built on 2017-10-24 from the following noto repository: 5 | ----- 6 | Repo: noto-emoji 7 | Tag: v2017-09-13-design-refresh 8 | Date: 2017-09-20 10:08:23 GMT 9 | Commit: d5ac67b140cc63aa512bbb3d164e15737dd97564 10 | 11 | Update color emoji font and assets for new design. 12 | 13 | This design is the one used in Android O. 14 | 15 | The blobs are dead. Long live the blobs. 16 | -------------------------------------------------------------------------------- /quadwriter/fonts/fallback-math/LICENSE_OFL.txt: -------------------------------------------------------------------------------- 1 | This Font Software is licensed under the SIL Open Font License, 2 | Version 1.1. 3 | 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font 14 | creation efforts of academic and linguistic communities, and to 15 | provide a free and open framework in which fonts may be shared and 16 | improved in partnership with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply to 25 | any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software 36 | components as distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, 39 | deleting, or substituting -- in part or in whole -- any of the 40 | components of the Original Version, by changing formats or by porting 41 | the Font Software to a new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, 49 | modify, redistribute, and sell modified and unmodified copies of the 50 | Font Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, in 53 | Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the 64 | corresponding Copyright Holder. This restriction only applies to the 65 | primary font name as presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created using 77 | the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /quadwriter/fonts/fallback-math/NotoSansMath-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/fallback-math/NotoSansMath-Regular.ttf -------------------------------------------------------------------------------- /quadwriter/fonts/fallback/LICENSE_OFL.txt: -------------------------------------------------------------------------------- 1 | This Font Software is licensed under the SIL Open Font License, 2 | Version 1.1. 3 | 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font 14 | creation efforts of academic and linguistic communities, and to 15 | provide a free and open framework in which fonts may be shared and 16 | improved in partnership with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply to 25 | any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software 36 | components as distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, 39 | deleting, or substituting -- in part or in whole -- any of the 40 | components of the Original Version, by changing formats or by porting 41 | the Font Software to a new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, 49 | modify, redistribute, and sell modified and unmodified copies of the 50 | Font Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, in 53 | Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the 64 | corresponding Copyright Holder. This restriction only applies to the 65 | primary font name as presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created using 77 | the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /quadwriter/fonts/fallback/bold-italic/NotoSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/fallback/bold-italic/NotoSans-BoldItalic.ttf -------------------------------------------------------------------------------- /quadwriter/fonts/fallback/bold/NotoSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/fallback/bold/NotoSans-Bold.ttf -------------------------------------------------------------------------------- /quadwriter/fonts/fallback/italic/NotoSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/fallback/italic/NotoSans-Italic.ttf -------------------------------------------------------------------------------- /quadwriter/fonts/fallback/regular/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/fallback/regular/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /quadwriter/fonts/heading/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 2 | with Reserved Font Name Fira Sans. 3 | 4 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 5 | with Reserved Font Name Fira Mono. 6 | 7 | Copyright (c) 2014, Telefonica S.A. 8 | 9 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 10 | This license is copied below, and is also available with a FAQ at: 11 | http://scripts.sil.org/OFL 12 | 13 | 14 | ----------------------------------------------------------- 15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 16 | ----------------------------------------------------------- 17 | 18 | PREAMBLE 19 | The goals of the Open Font License (OFL) are to stimulate worldwide 20 | development of collaborative font projects, to support the font creation 21 | efforts of academic and linguistic communities, and to provide a free and 22 | open framework in which fonts may be shared and improved in partnership 23 | with others. 24 | 25 | The OFL allows the licensed fonts to be used, studied, modified and 26 | redistributed freely as long as they are not sold by themselves. The 27 | fonts, including any derivative works, can be bundled, embedded, 28 | redistributed and/or sold with any software provided that any reserved 29 | names are not used by derivative works. The fonts and derivatives, 30 | however, cannot be released under any other type of license. The 31 | requirement for fonts to remain under this license does not apply 32 | to any document created using the fonts or their derivatives. 33 | 34 | DEFINITIONS 35 | "Font Software" refers to the set of files released by the Copyright 36 | Holder(s) under this license and clearly marked as such. This may 37 | include source files, build scripts and documentation. 38 | 39 | "Reserved Font Name" refers to any names specified as such after the 40 | copyright statement(s). 41 | 42 | "Original Version" refers to the collection of Font Software components as 43 | distributed by the Copyright Holder(s). 44 | 45 | "Modified Version" refers to any derivative made by adding to, deleting, 46 | or substituting -- in part or in whole -- any of the components of the 47 | Original Version, by changing formats or by porting the Font Software to a 48 | new environment. 49 | 50 | "Author" refers to any designer, engineer, programmer, technical 51 | writer or other person who contributed to the Font Software. 52 | 53 | PERMISSION & CONDITIONS 54 | Permission is hereby granted, free of charge, to any person obtaining 55 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 56 | redistribute, and sell modified and unmodified copies of the Font 57 | Software, subject to the following conditions: 58 | 59 | 1) Neither the Font Software nor any of its individual components, 60 | in Original or Modified Versions, may be sold by itself. 61 | 62 | 2) Original or Modified Versions of the Font Software may be bundled, 63 | redistributed and/or sold with any software, provided that each copy 64 | contains the above copyright notice and this license. These can be 65 | included either as stand-alone text files, human-readable headers or 66 | in the appropriate machine-readable metadata fields within text or 67 | binary files as long as those fields can be easily viewed by the user. 68 | 69 | 3) No Modified Version of the Font Software may use the Reserved Font 70 | Name(s) unless explicit written permission is granted by the corresponding 71 | Copyright Holder. This restriction only applies to the primary font name as 72 | presented to the users. 73 | 74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 75 | Software shall not be used to promote, endorse or advertise any 76 | Modified Version, except to acknowledge the contribution(s) of the 77 | Copyright Holder(s) and the Author(s) or with their explicit written 78 | permission. 79 | 80 | 5) The Font Software, modified or unmodified, in part or in whole, 81 | must be distributed entirely under this license, and must not be 82 | distributed under any other license. The requirement for fonts to 83 | remain under this license does not apply to any document created 84 | using the Font Software. 85 | 86 | TERMINATION 87 | This license becomes null and void if any of the above conditions are 88 | not met. 89 | 90 | DISCLAIMER 91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 99 | OTHER DEALINGS IN THE FONT SOFTWARE. 100 | -------------------------------------------------------------------------------- /quadwriter/fonts/heading/bold-italic/fira-sans-light-bold-italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/heading/bold-italic/fira-sans-light-bold-italic.otf -------------------------------------------------------------------------------- /quadwriter/fonts/heading/bold/fira-sans-light-bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/heading/bold/fira-sans-light-bold.otf -------------------------------------------------------------------------------- /quadwriter/fonts/heading/italic/fira-sans-light-italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/heading/italic/fira-sans-light-italic.otf -------------------------------------------------------------------------------- /quadwriter/fonts/heading/regular/fira-sans-light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/heading/regular/fira-sans-light.otf -------------------------------------------------------------------------------- /quadwriter/fonts/text/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2014-2018 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | 5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /quadwriter/fonts/text/bold-italic/SourceSerifPro-BoldIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/text/bold-italic/SourceSerifPro-BoldIt.otf -------------------------------------------------------------------------------- /quadwriter/fonts/text/bold/SourceSerifPro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/text/bold/SourceSerifPro-Bold.otf -------------------------------------------------------------------------------- /quadwriter/fonts/text/italic/SourceSerifPro-It.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/text/italic/SourceSerifPro-It.otf -------------------------------------------------------------------------------- /quadwriter/fonts/text/regular/SourceSerifPro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbutterick/quad/9c6e450b0429ad535edf9480a4b76e5c23161ec0/quadwriter/fonts/text/regular/SourceSerifPro-Regular.otf -------------------------------------------------------------------------------- /quadwriter/html.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | (require "lang-helper.rkt" 3 | "tags.rkt" 4 | pollen/decode 5 | (only-in "markdown.rkt" doc-proc)) 6 | (provide #%top #%datum #%app #%top-interaction 7 | (all-from-out "tags.rkt") 8 | q) 9 | 10 | (make-module-begin 11 | (λ (exprs) (doc-proc (decode-paragraphs exprs #:force? #true)))) 12 | 13 | (module reader racket/base 14 | (require "lang-helper.rkt") 15 | (provide read-syntax get-info) 16 | (define get-info get-info-texty) 17 | (define read-syntax 18 | (make-read-syntax 'quadwriter/markup 19 | (λ (path-string ip) 20 | (syntax->datum (quad-at-reader path-string ip)))))) -------------------------------------------------------------------------------- /quadwriter/image.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require "struct.rkt" 3 | "attrs.rkt" 4 | "param.rkt" 5 | "debug.rkt" 6 | quad/position 7 | pitfall 8 | quad/quad) 9 | (provide (all-defined-out)) 10 | 11 | 12 | 13 | (define (convert-image-quad q) 14 | (define path-string (quad-ref q :image-file)) 15 | (unless (file-exists? path-string) 16 | (raise-argument-error 'create-image-quad "image path that exists" path-string)) 17 | (define img-obj (open-image (current-pdf) path-string)) 18 | (define img-width ($img-width img-obj)) 19 | (define img-height ($img-height img-obj)) 20 | (match-define (list layout-width layout-height) 21 | (match (list (quad-ref q :image-width) (quad-ref q :image-height)) 22 | [(list (? number? w) (? number? h)) (list w h)] 23 | [(list #false (? number? h)) 24 | (define ratio (/ h img-height)) 25 | (list (* ratio img-width) h)] 26 | [(list (? number? w) #false) 27 | (define ratio (/ w img-width)) 28 | (list w (* ratio img-height))] 29 | [(list #false #false) (list img-width img-height)])) 30 | (quad-copy image-quad q:image 31 | [attrs (let ([h (hash-copy (quad-attrs q))]) 32 | ;; defeat 'bi 'bo positioning by removing font reference 33 | (hash-set! h font-path-key #false) 34 | ;; save the img-obj for later 35 | (hash-set! h :image-object img-obj) 36 | h)] 37 | [size (pt layout-width layout-height)])) 38 | 39 | (define (q:image-draw q doc) 40 | (define img (quad-ref q :image-object)) 41 | (match-define (list x y) (quad-origin q)) 42 | (match-define (list w h) (size q)) 43 | (image doc img x y 44 | #:width w 45 | #:height h)) 46 | 47 | (define (q:image-draw-end q doc) 48 | (when (draw-debug-image?) 49 | (draw-debug q doc "orange" "orange"))) 50 | 51 | (define q:image (q #:type image-quad 52 | #:from 'bo 53 | #:to 'bi 54 | #:tag 'image 55 | #:printable #true 56 | #:draw q:image-draw 57 | #:draw-end q:image-draw-end)) -------------------------------------------------------------------------------- /quadwriter/keep.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require quad/quad 3 | quad/util 4 | "attrs.rkt" 5 | "struct.rkt") 6 | (provide (all-defined-out)) 7 | 8 | 9 | 10 | (define (make-nobreak! q) (quad-set! q :no-colbr #true)) ; scooperates with col-wrap 11 | 12 | (define (do-keep-with-next! reversed-lines) 13 | ;; paints nobreak onto the kwn line itself, 14 | ;; and any line spacers that follow (could be one or more) 15 | ;; (we are iterating backward, so the geometrically previous ln follows the spacer) 16 | (define (is-kwn-line? ln) (quad-ref ln :keep-with-next)) 17 | (let loop ([lines (reverse reversed-lines)]) 18 | (unless (null? lines) 19 | (match lines 20 | [(list* (? is-kwn-line? kwn) (? line-spacer-quad? lsqs) ..1 rest) 21 | (for-each make-nobreak! (cons kwn lsqs)) 22 | (loop rest)] 23 | [(cons ln rest) (loop rest)])))) 24 | 25 | (define (apply-keeps lines) 26 | (define groups-of-lines (contiguous-group-by (λ (x) (quad-ref x :display)) lines)) 27 | (for*/fold ([reversed-lines null] 28 | #:result (begin 29 | (do-keep-with-next! reversed-lines) 30 | (reverse reversed-lines))) 31 | ([group (in-list groups-of-lines)] 32 | [group-len (in-value (length group))] 33 | [(ln idx0) (in-indexed group)]) 34 | (define idx (add1 idx0)) 35 | ;; always catch last line of block in this case 36 | ;; so later cases are guaranteed to have earlier lines. 37 | (define keep-first (quad-ref ln :keep-first-lines)) 38 | (define keep-last (quad-ref ln :keep-last-lines)) 39 | (unless (eq? idx group-len) 40 | (when (or 41 | ;; if we have keep all we can skip :keep-first and :keep-last cases 42 | (or (equal? keep-first "all") (equal? keep-last "all")) 43 | ;; to keep n lines, we only paint the first n - 1 44 | ;; (because each nobr line sticks to the next) 45 | (and (number? keep-first) (< idx keep-first)) 46 | (and (number? keep-last) (< (- group-len keep-last) idx))) 47 | (make-nobreak! ln))) 48 | (cons ln reversed-lines))) -------------------------------------------------------------------------------- /quadwriter/lang.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | (require pollen/tag "lang-helper.rkt") 3 | (provide (except-out (all-from-out racket/base) #%module-begin)) 4 | 5 | (define (doc-proc strs) (apply q strs)) 6 | (make-module-begin doc-proc) 7 | 8 | -------------------------------------------------------------------------------- /quadwriter/log.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require racket/logging) 3 | (provide (all-defined-out) with-logging-to-port) 4 | 5 | ;; creates `quadwriter-logger` and associated functions: 6 | ;; log-quadwriter-fatal, log-quadwriter-error, log-quadwriter-warning, 7 | ;; log-quadwriter-info, and log-quadwriter-debug 8 | (define-logger quadwriter) 9 | 10 | (define-syntax-rule (time-log NAME EXPR) 11 | (let-values ([(res ms real-ms gc) (time-apply (λ () EXPR) null)]) 12 | (log-quadwriter-info (format "~a: ~ams" 'NAME real-ms)) 13 | (car res))) -------------------------------------------------------------------------------- /quadwriter/main.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | (require "core.rkt") 3 | (provide (all-from-out "core.rkt")) 4 | 5 | (module docmod racket/base 6 | (define doc 'just-for-label) 7 | (provide doc)) 8 | (require (for-label 'docmod)) 9 | (provide (for-label doc)) ; stub for scribble labels 10 | 11 | (module reader racket/base 12 | (require "lang-helper.rkt") 13 | (provide (rename-out [rs read-syntax]) get-info) 14 | (define get-info get-info-rackety) 15 | (define rs (make-read-syntax 'quadwriter/lang 16 | (λ (path ip) 17 | (for/list ([tok (in-port read ip)]) 18 | tok))))) -------------------------------------------------------------------------------- /quadwriter/markdown.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | (require racket/list 3 | racket/match 4 | quadwriter/core 5 | "tags.rkt" 6 | "lang-helper.rkt") 7 | (provide (all-defined-out) 8 | #%app #%top #%datum #%top-interaction require 9 | (all-from-out "tags.rkt")) 10 | 11 | (define (doc-proc exprs) 12 | (define strs (match exprs 13 | [(? null?) '(" ")] ; single nonbreaking space, so something prints 14 | [strs strs])) 15 | ;; markdown parser returns list of paragraphs 16 | (root null (match strs 17 | [(list str) strs] 18 | [_ (add-between strs (list para-break) 19 | #:before-first (list para-break) 20 | #:after-last (list para-break) 21 | #:splice? #true)]))) 22 | (make-module-begin doc-proc) 23 | 24 | (module reader racket/base 25 | (require racket/port markdown "lang-helper.rkt") 26 | (provide read-syntax get-info) 27 | (define get-info get-info-texty) 28 | (define read-syntax (make-read-syntax 'quadwriter/markdown 29 | (λ (path-string p) (xexpr->parse-tree 30 | (parameterize ([current-strict-markdown? #t]) 31 | (parse-markdown (port->string p)))))))) -------------------------------------------------------------------------------- /quadwriter/markup.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket/base 2 | ;; `quadwriter/markup` was a misleading name for this dialect 3 | ;; it is now `quadwriter/html`, 4 | ;; but we will make `quadwriter/markup` work the same way for compatibility 5 | (require "html.rkt") 6 | (provide (all-from-out "html.rkt")) 7 | 8 | (module reader racket/base 9 | (require (submod "html.rkt" reader)) 10 | (provide (all-from-out (submod "html.rkt" reader)))) -------------------------------------------------------------------------------- /quadwriter/page.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require "struct.rkt" 3 | "attrs.rkt" 4 | "param.rkt" 5 | "debug.rkt" 6 | "font.rkt" 7 | "string.rkt" 8 | quad/base 9 | racket/date 10 | pitfall) 11 | (provide (all-defined-out)) 12 | 13 | 14 | 15 | (define default-page-size "letter") 16 | (define default-page-orientation "tall") 17 | (define (page-draw-start q doc) 18 | (match-define (list page-width page-height) (parse-page-size q)) 19 | (add-page doc page-width page-height) 20 | (scale doc (zoom-factor) (zoom-factor)) 21 | (draw-debug q doc "purple" "purple" 1)) 22 | 23 | 24 | (define q:page (make-quad 25 | #:type page-quad 26 | #:tag 'page 27 | #:from-parent 'nw 28 | #:draw-start page-draw-start)) 29 | 30 | 31 | (define (parse-page-size q) 32 | ;; page size can be specified by name, or measurements. 33 | ;; explicit measurements from page-height and page-width supersede those from page-size. 34 | (match-define (list page-width page-height) 35 | (for/list ([k (list :page-width :page-height)]) 36 | (and (quad? q) (match (quad-ref q k) 37 | [#false #false] 38 | [val (inexact->exact (floor val))])))) 39 | (resolve-page-size 40 | (or (debug-page-width) page-width) 41 | (or (debug-page-height) page-height) 42 | (quad-ref q :page-size default-page-size) 43 | (quad-ref q :page-orientation default-page-orientation))) 44 | 45 | 46 | (define (draw-page-footer q doc) 47 | (match-define (list x y) (quad-origin q)) 48 | (font-size doc (quad-ref q :font-size)) 49 | (font doc (path->string (quad-ref q font-path-key default-font-face))) 50 | (fill-color doc default-font-color) 51 | (define str (or (quad-ref q :footer-text) 52 | (format "~a · ~a at ~a" (quad-ref q :page-number 0) 53 | (if (quadwriter-test-mode) "test" (quad-ref q :doc-title "untitled")) 54 | (date->string (if (quadwriter-test-mode) (seconds->date 0 #f) (current-date)) #t)))) 55 | (text doc str x y) 56 | #;(set-quad-size! q (make-size-promise-for-string q str))) 57 | 58 | (define (make-footer-quad col-q page-idx path) 59 | (define-values (dir name _) (split-path (path-replace-extension path #""))) 60 | (define attrs (let ([attrs (make-hasheq)] 61 | [cltq (current-top-level-quad)]) 62 | (hash-set*! attrs 63 | :footer-text (quad-ref col-q :footer-text) 64 | :page-number (+ (quad-ref col-q :page-number-start (add1 (section-pages-used))) (sub1 page-idx)) 65 | :doc-title (string-titlecase (path->string name)) 66 | ;; we get font & line-height from cltq 67 | ;; because these are not block attrs 68 | ;; so they are not propagated upward from col-q 69 | :font-size (* 0.8 (quad-ref cltq :font-size)) 70 | :line-height (quad-ref cltq :line-height) 71 | :font-path (quad-ref cltq :font-path)) 72 | attrs)) 73 | (make-quad #:size (pt 50 default-line-height) 74 | #:attrs attrs 75 | #:from-parent 'sw 76 | #:to 'nw 77 | #:elems null 78 | #:shift (pt 0 (* 1.5 default-line-height)) 79 | #:printable #true 80 | #:draw draw-page-footer 81 | #:draw-end (λ (q doc) 82 | (when draw-debug-line? 83 | (draw-debug q doc "goldenrod" "goldenrod"))))) 84 | 85 | (define ((page-wrap-finish make-page-quad path) cols q-before q-after page-idx) 86 | (define pq (make-page-quad (+ (section-pages-used) page-idx))) 87 | ;; get attrs from cols if we can, otherwise try q-after or q-before 88 | (define q-for-attrs (cond 89 | [(pair? cols) (car cols)] 90 | [q-after] 91 | [q-before] 92 | [else (raise-argument-error 'page-wrap-finish "quad with attrs" (list cols q-after q-before))])) 93 | (define elems 94 | (append 95 | (match (quad-ref q-for-attrs :footer-display #false) 96 | [(or #false "none") null] 97 | [_ (list (make-footer-quad q-for-attrs page-idx path))]) 98 | (from-parent cols 'nw))) 99 | (list (quad-update! pq 100 | [elems elems] 101 | [attrs (copy-block-attrs (cond 102 | [q-for-attrs => quad-attrs] 103 | [else (hash)]) 104 | (hash-copy (quad-attrs pq)))]))) 105 | 106 | (define (page-wrap qs width [make-page-quad (λ (x) (quad-copy page-quad q:page))]) 107 | (unless (positive? width) 108 | (raise-argument-error 'page-wrap "positive number" width)) 109 | (wrap qs width 110 | #:soft-break #true 111 | #:hard-break page-break-quad? 112 | #:no-break (λ (q) (quad-ref q :no-pbr)) 113 | #:distance (λ (q dist-so-far wrap-qs) (sum-x wrap-qs)) 114 | #:finish-wrap (page-wrap-finish make-page-quad (pdf-output-path (current-pdf))))) -------------------------------------------------------------------------------- /quadwriter/para.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require "attrs.rkt" 3 | "break.rkt" 4 | "string.rkt" 5 | "struct.rkt" 6 | quad/quad 7 | quad/position) 8 | (provide (all-defined-out)) 9 | 10 | (define (insert-first-line-indents qs-in) 11 | ;; first line indents are quads inserted at the beginning of a paragraph 12 | ;; (that is, just after a paragraph break) 13 | ;; they need to be installed before line wrap 14 | ;; to be compatible with first-fit and best-fit. 15 | 16 | ;; stick a pbr on the front if there isn't one already 17 | ;; because of the "lookahead" style of iteration 18 | (define qs (match qs-in 19 | [(cons (? para-break-quad?) _) qs-in] 20 | [_ (cons q:page-break qs-in)])) 21 | (apply append 22 | (for/list ([q (in-list qs)] 23 | [next-q (in-list (cdr qs))]) 24 | (match (and (para-break-quad? q) (quad-ref next-q :first-line-indent 0)) 25 | [(or #false 0) (list next-q)] 26 | [indent-val (list (make-quad #:from 'bo 27 | #:to 'bi 28 | #:draw-end q:string-draw-end 29 | #:type first-line-indent-quad 30 | #:attrs (quad-attrs next-q) 31 | #:size (pt indent-val 10)) next-q)])))) -------------------------------------------------------------------------------- /quadwriter/param.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (provide (all-defined-out)) 3 | (define current-doc (make-parameter #false)) 4 | 5 | (define current-pdf (make-parameter #false)) 6 | (define current-line-wrap (make-parameter #f)) ; because kp is slow and maybe we want to disable for "draft" mode 7 | (define current-top-level-quad (make-parameter #f)) 8 | (define section-pages-used (make-parameter 0)) 9 | 10 | (define quadwriter-test-mode (make-parameter #f)) ; used during rackunit to suppress nondeterministic elements, like timestamp in header 11 | 12 | (define zoom-factor (make-parameter 1)) 13 | (define log-clipping? (make-parameter 'warn)) -------------------------------------------------------------------------------- /quadwriter/section.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require "struct.rkt" 3 | quad/quad) 4 | (provide (all-defined-out)) 5 | 6 | (define q:section (make-quad #:type section-quad 7 | #:tag 'section)) 8 | -------------------------------------------------------------------------------- /quadwriter/struct.rkt: -------------------------------------------------------------------------------- 1 | #lang debug racket 2 | (require quad/quad) 3 | (provide (all-defined-out)) 4 | 5 | (define-quad break-quad) 6 | (define-quad line-break-quad break-quad) 7 | (define-quad para-break-quad line-break-quad) 8 | (define-quad hr-break-quad line-break-quad) 9 | (define-quad column-break-quad line-break-quad) 10 | (define-quad page-break-quad column-break-quad) 11 | (define-quad section-break-quad page-break-quad) 12 | 13 | (define-quad line-quad) 14 | (define-quad line-spacer-quad line-break-quad) 15 | 16 | (define-quad filler-quad) 17 | (define-quad offsetter-quad) 18 | 19 | (define-quad column-quad) 20 | (define-quad column-spacer-quad) 21 | 22 | (define-quad page-quad) 23 | 24 | (define-quad doc-quad) 25 | 26 | (define-quad section-quad) 27 | 28 | (define-quad block-quad) 29 | 30 | (define-quad first-line-indent-quad) 31 | 32 | (define-quad string-quad) 33 | (define-quad image-quad) 34 | (define-quad draw-quad) 35 | -------------------------------------------------------------------------------- /quadwriter/test.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | #;(require qtest/all) --------------------------------------------------------------------------------