diff --git a/quad/quad/atomize.rkt b/quad/quad/atomize.rkt index 46b1ec32..3a79eb60 100644 --- a/quad/quad/atomize.rkt +++ b/quad/quad/atomize.rkt @@ -6,7 +6,8 @@ (define (update-with base-hash . update-hashes) ;; starting with base-hash, add or update keys found in update-hashes - (apply hasheq (flatten (map hash->list (list* base-hash update-hashes))))) + (for/hasheq ([(k v) (in-dict (append-map hash->list (list* base-hash update-hashes)))]) + (values k v))) (module+ test (check-equal? @@ -39,7 +40,7 @@ (define atomic-quads (let loop ([x (if (string? qx) (q qx) qx)][attrs (current-default-attrs)]) (match x - [(? char? c) (list (q attrs c))] + [(? char? c) (list (q (hash-set attrs 'id (gensym)) c))] [(? string?) (append* (for/list ([c (in-string x)]) ;; strings are exploded (loop c attrs)))] [($quad this-attrs elems) ;; qexprs with attributes are recursed diff --git a/quad/quad/break.rkt b/quad/quad/break.rkt index 92ef4ab7..27fe3b63 100644 --- a/quad/quad/break.rkt +++ b/quad/quad/break.rkt @@ -2,17 +2,19 @@ (require racket/contract racket/list racket/match txexpr sugar/debug sugar/define sugar/list racket/promise racket/function (only-in racket/control call/prompt) racket/future "param.rkt" "qexpr.rkt" "atomize.rkt" "quad.rkt" "generic.rkt" "position.rkt") +(define distance-cache (make-hasheq)) (define/contract (distance q) (any/c . -> . real?) - ;; linear distance from in point to out point - (cond - [(quad? q) - (match-define (list ∆x ∆y) (map - (out-point q) (in-point q))) - (cond - [(zero? ∆x) ∆y] - [(zero? ∆y) ∆x] - [else (sqrt (+ (* ∆x ∆x) (* ∆y ∆y)))])] - [else 0])) + (hash-ref! distance-cache (hash-ref (attrs q) 'id q) + (λ () + (cond + [(quad? q) + (match-define (list ∆x ∆y) (map - (out-point q) (in-point q))) + (cond + [(zero? ∆x) ∆y] + [(zero? ∆y) ∆x] + [else (sqrt (+ (* ∆x ∆x) (* ∆y ∆y)))])] + [else 0])))) (define+provide/contract (break xs @@ -67,22 +69,22 @@ [else (define-values (head tail) (splitf-at xs (λ (x) (not (hard-break? x))))) (values (cons (future (λ () (cleanup-wraplist (break-softs head - target-size - debug - break-val - soft-break? - finish-wrap-proc)))) wraps) tail)]))) + target-size + debug + break-val + soft-break? + finish-wrap-proc)))) wraps) tail)]))) (append (if break-before? (list break-val) empty) (cleanup-wraplist wraps) (if break-after? (list break-val) empty))) (define (nonprinting-at-start? x) (if (quad? x) (not (printable? x 'start)) #t)) (define (nonprinting-at-end? x) (if (quad? x) (not (printable? x 'end)) #t)) (define (break-softs xs - target-size - debug - break-val - soft-break? - finish-wrap-proc) + target-size + debug + break-val + soft-break? + finish-wrap-proc) (define start-signal (gensym)) (define last-soft-break-k #f) (define (capture-soft-break-k!) diff --git a/quad/quad/charter.ttf b/quad/quad/charter.ttf new file mode 100644 index 00000000..33c6d7cb Binary files /dev/null and b/quad/quad/charter.ttf differ diff --git a/quad/quad/position.rkt b/quad/quad/position.rkt index 238f5b43..8ba4a3aa 100644 --- a/quad/quad/position.rkt +++ b/quad/quad/position.rkt @@ -25,19 +25,21 @@ (define fonts (make-hash)) (hash-ref! fonts p (λ () (openSync p)))) +(define ascender-cache (make-hash)) (define/contract (ascender q) (quad? . -> . real?) (define p (hash-ref (attrs q) 'font "Courier")) (unless p (error 'ascender-no-font-key)) - (ascent (get-font p))) + (hash-ref! ascender-cache p (λ () (ascent (get-font p))))) +(define units-cache (make-hash)) (define/contract (units-per-em q) (quad? . -> . real?) (define p (hash-ref (attrs q) 'font "Courier")) (unless p (error 'units-per-em-no-font-key)) - (unitsPerEm (get-font p))) + (hash-ref! units-cache p (λ () (unitsPerEm (get-font p))))) (define (fontsize q) ;; this needs to not default to 0 diff --git a/quad/quad/typewriter-raw.rkt b/quad/quad/typewriter-raw.rkt new file mode 100644 index 00000000..a02b4ef3 --- /dev/null +++ b/quad/quad/typewriter-raw.rkt @@ -0,0 +1,15 @@ +#lang debug br +(require racket/runtime-path pitfall/document) +(define-runtime-path fira "fira.ttf") +(define PS "typewriter-raw.pdf") +(define doc (make-object PDFDocument + (hasheq 'compress #t + 'autoFirstPage #t + 'size '(300 200)))) +(time (send* doc + [pipe (open-output-file PS #:exists 'replace)] + [registerFont "Fira" (path->string fira)] + [font "Fira"] + [fontSize 12] + [text "Hello world" 36 36])) +(send doc end) \ No newline at end of file diff --git a/quad/quad/typewriter-test.rkt b/quad/quad/typewriter-test.rkt index f7405846..17e687ff 100644 --- a/quad/quad/typewriter-test.rkt +++ b/quad/quad/typewriter-test.rkt @@ -1,7 +1,8 @@ #lang quad/typewriter -◊quad[#:fontsize "11"]{Hello world} +◊;quad[#:fontsize "11"]{Hello world} ◊;quad[#:fontsize "11"]{◊quad[#:link "http://beautifulracket.com"]{An expression that} is not a value can ◊quad[#:fontsize "22"]{always} ◊quad[#:fontsize "7"]{be partitioned} into} -◊;quad[#:fontsize "11"]{◊quad[#:link "http://beautifulracket.com"]{An expression that} is not a value can ◊quad[#:fontsize "22"]{always} ◊quad[#:fontsize "7"]{be partitioned} into two parts: a redex, which is the part that changed in a single-step simplification (highlighted), and the continuation, which is the evaluation context surrounding an expression. In (- 4 (+ 1 1)), the redex is (+ 1 1), and the continuation is (- 4 []), where [] takes the place of the redex. That is, the continuation says how to "continue" after the redex is reduced to a value."} \ No newline at end of file + +◊quad[#:fontsize "11"]{◊quad[#:link "http://beautifulracket.com"]{An expression that} is not a value can ◊quad[#:fontsize "22"]{always} ◊quad[#:fontsize "7"]{be partitioned} into two parts: a redex, which is the part that changed in a single-step simplification (highlighted), and the continuation, which is the evaluation context surrounding an expression. In (- 4 (+ 1 1)), the redex is (+ 1 1), and the continuation is (- 4 []), where [] takes the place of the redex. That is, the continuation says how to "continue" after the redex is reduced to a value.} \ No newline at end of file diff --git a/quad/quad/typewriter.rkt b/quad/quad/typewriter.rkt index b49454ed..ce4f1a08 100644 --- a/quad/quad/typewriter.rkt +++ b/quad/quad/typewriter.rkt @@ -5,12 +5,12 @@ (provide (rename-out [mb #%module-begin]) (except-out (all-from-out br/quicklang) #%module-begin)) -(define-runtime-path fira "fira.ttf") +(define-runtime-path charter "charter.ttf") (define soft-break? (λ (q) (and (quad? q) (memv (car (elems q)) '(#\space #\- #\u00AD))))) (struct $shim $quad () #:transparent) (struct $char $quad () #:transparent) -(define util-doc (make-object PDFDocument)) +(define current-doc (make-parameter #f)) (define (draw-debug q doc) (send doc save) (send doc lineWidth 0.25) @@ -28,16 +28,17 @@ ($char (hash-set* (attrs q) 'in 'bi 'out 'bo - 'font fira + 'font charter 'size (delay (let ([fontsize (string->number (hash-ref (attrs q) 'fontsize "12"))] [str (apply string (elems q))]) - (send util-doc fontSize (string->number (hash-ref (attrs q) 'fontsize "12"))) - (send util-doc font fira) + (send* (current-doc) + [fontSize (string->number (hash-ref (attrs q) 'fontsize "12"))] + [font (path->string charter)]) (list - (send util-doc widthOfString str) - (send util-doc currentLineHeight)))) + (send (current-doc) widthOfString str) + (send (current-doc) currentLineHeight)))) 'printable? (case (car (elems q)) [(#\u00AD) (λ (sig) (memq sig '(end)))] [(#\space) (λ (sig) (not (memq sig '(start end))))] @@ -51,8 +52,8 @@ => (λ (url-str) (apply as-link doc str url-str (origin q)))] [else - (println str) - (time-name send-text (send/apply doc text str (origin q)))])))) (elems q))) + #;(println str) + (send/apply doc text str (origin q))])))) (elems q))) (struct $line $quad () #:transparent) (struct $page $quad () #:transparent) (struct $doc $quad () #:transparent) @@ -63,7 +64,7 @@ (define (run-attrs-match left right) (define missing (gensym)) (for/and ([k (in-list '(link weight fontsize))]) - (equal? (hash-ref (attrs left) k missing) (hash-ref (attrs right) k missing)))) + (equal? (hash-ref (attrs left) k missing) (hash-ref (attrs right) k missing)))) (define (consolidate-runs pcs) (for/fold ([runs empty] @@ -73,12 +74,13 @@ #:break (empty? pcs)) (define-values (run-pcs rest) (splitf-at pcs (λ (p) (run-attrs-match (car pcs) p)))) (define new-run ($char (hash-set (attrs (car pcs)) - 'size (delay (apply map + (map size run-pcs)))) + 'size (delay (list (pt-x (apply map + (map size run-pcs))) + (pt-y (size (car pcs)))))) (append-map elems run-pcs))) (values (cons new-run runs) rest))) (define line-height 16) -(define consolidate-into-runs? #f) +(define consolidate-into-runs? #t) (define (line-wrap xs size [debug #f]) (break xs size debug #:break-val (make-break #\newline) @@ -109,14 +111,14 @@ (send doc fontSize 10) (define str (string-append "page " (number->string page-count))) ;; page number - #;(as-link doc str "https://practicaltypography.com" 10 10) + (as-link doc str "https://practicaltypography.com" 10 10) (set! page-count (add1 page-count)))) '(#\page))) (define (page-wrap xs size [debug #f]) (break xs size debug #:break-before? #t #:break-val pb #:soft-break-proc $break? - #:finish-wrap-proc (λ (pcs) (list ($page (hasheq) (filter-not $break? pcs)))))) + #:finish-wrap-proc (λ (pcs) (list ($page (hasheq 'offset '(36 36)) (filter-not $break? pcs)))))) (define (typeset qarg) (define chars 25) @@ -124,30 +126,33 @@ (define lines-per-page (* 4 line-height)) (let* ([x (time-name line-wrap (line-wrap (map charify (atomize qarg)) line-width))] [x (time-name page-wrap (page-wrap x lines-per-page))] - [x (time-name position (position ($doc (hasheq 'origin '(36 36)) x)))]) + [x (time-name position (position ($doc (hasheq) x)))]) x)) (provide quad) (define quad (default-tag-function 'quad)) +(define (run qin [path "test.pdf"]) + (define doc (time-name make-doc + (make-object PDFDocument + (hasheq 'compress #t + 'autoFirstPage #f + 'size '(300 200))))) + (parameterize ([current-doc doc]) + (time-name config-doc + (send* doc + [pipe (open-output-file path #:exists 'replace)] + [font (path->string charter)] + [fontSize 12])) + (define q (typeset qin)) + (time-name draw (draw q doc)) + (time-name end-doc (send doc end)))) + (define-macro (mb . ARGS) (with-pattern ([PS (syntax-property #'ARGS 'ps)]) #'(#%module-begin - (let ([doc (time-name make-doc - (make-object PDFDocument - (hasheq 'compress #t - 'autoFirstPage #f - 'size '(300 200))))]) - (time-name config-doc - (send* doc - [pipe (open-output-file PS #:exists 'replace)] - [registerFont "Fira" (path->string fira)] - [font "Fira"] - [fontSize 12])) - (define q (typeset (qexpr->quad (quad . ARGS)))) - (time-name draw (draw q doc)) - (time-name end-doc (send doc end))) + (run (qexpr->quad (quad . ARGS)) PS) (void)))) (module reader syntax/module-reader @@ -164,4 +169,4 @@ #:syntax? #t #:inside? #t #:command-char #\◊)) - (syntax-property (quad-at-reader path-string p) 'ps (path-replace-extension path-string #".pdf")))) \ No newline at end of file + (syntax-property (quad-at-reader path-string p) 'ps (path-replace-extension path-string #".pdf"))))