From 230e56836be78866d1d24b35085bd74bac075515 Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Thu, 11 Apr 2019 14:58:57 -0700 Subject: [PATCH] quadwriter --- quad/qtest/docs.rkt | 2 +- quad/qtest/fark.rkt | 8 +- quad/qtest/hyphenate.rkt | 2 +- quad/qtest/typewriter.rkt | 2 +- .../markdown.rkt => quadwriter/core.rkt} | 180 +++--------------- quad/quadwriter/fonts/charter.otf | Bin 0 -> 26968 bytes quad/quadwriter/main.rkt | 31 +++ quad/quadwriter/markdown.rkt | 156 +++++++++++++++ 8 files changed, 215 insertions(+), 166 deletions(-) rename quad/{qtest/markdown.rkt => quadwriter/core.rkt} (80%) create mode 100644 quad/quadwriter/fonts/charter.otf create mode 100644 quad/quadwriter/main.rkt create mode 100644 quad/quadwriter/markdown.rkt diff --git a/quad/qtest/docs.rkt b/quad/qtest/docs.rkt index 7dd3529f..456cb578 100644 --- a/quad/qtest/docs.rkt +++ b/quad/qtest/docs.rkt @@ -1,4 +1,4 @@ -#lang qtest/markdown +#lang quadwriter/markdown # Macros diff --git a/quad/qtest/fark.rkt b/quad/qtest/fark.rkt index 3c669358..9569b172 100644 --- a/quad/qtest/fark.rkt +++ b/quad/qtest/fark.rkt @@ -1,8 +1,4 @@ -#lang qtest/markdown +#lang quadwriter -`(6 5)` -`(65)` - -The result of the above expression should be `(6 5)`. The naive -expansion of this use of `swap`, however, is \ No newline at end of file +◊q[#:link "https://beautifulracket.com"]{Hello world} \ No newline at end of file diff --git a/quad/qtest/hyphenate.rkt b/quad/qtest/hyphenate.rkt index ca643653..492158e3 100644 --- a/quad/qtest/hyphenate.rkt +++ b/quad/qtest/hyphenate.rkt @@ -1,4 +1,4 @@ -#lang qtest/markdown +#lang quadwriter/markdown # Hyphenate diff --git a/quad/qtest/typewriter.rkt b/quad/qtest/typewriter.rkt index f1770327..a59259b2 100644 --- a/quad/qtest/typewriter.rkt +++ b/quad/qtest/typewriter.rkt @@ -3,7 +3,7 @@ (require racket/promise racket/list sugar/debug pitfall/pdf pitfall/vector pitfall/font pitfall/annotation pitfall/color pitfall/text fontland/font racket/runtime-path pollen/tag) (provide (rename-out [mb #%module-begin]) (except-out (all-from-out racket) #%module-begin)) -(define-runtime-path charter "fonts/charter.ttf") +(define-runtime-path charter "fonts/charter/charter.otf") (define (soft-break? q) (and (quad? q) diff --git a/quad/qtest/markdown.rkt b/quad/quadwriter/core.rkt similarity index 80% rename from quad/qtest/markdown.rkt rename to quad/quadwriter/core.rkt index d19ce9df..2bd6c96e 100644 --- a/quad/qtest/markdown.rkt +++ b/quad/quadwriter/core.rkt @@ -1,114 +1,25 @@ #lang debug racket/base -(require (for-syntax racket/base) txexpr racket/runtime-path racket/path racket/string racket/promise racket/match racket/list - pitfall quad sugar/debug pollen/tag racket/unsafe/ops hyphenate) -(provide (except-out (all-from-out racket/base) #%module-begin) - (rename-out [mb #%module-begin]) - p id strong em attr-list h1 h2 h3 h4 h5 h6 - ol li ul rsquo lsquo rdquo ldquo hellip ndash mdash - hr - code pre a blockquote) - -(define rsquo "’") -(define rdquo "”") -(define lsquo "‘") -(define ldquo "“") -(define hellip "…") -(define ndash "–") -(define mdash "—") - -(define (root attrs exprs) - (qexpr (append `(#;(first-line-indent "12") - #;(line-align "center") - (line-wrap "kp") - (line-height "17") - #;(line-align-last "center")) attrs) exprs)) - -(define-tag-function (p attrs exprs) - ;; no font-family so that it adopts whatever the surrounding family is - (qexpr (append `((keep-first "2")(keep-last "3") (line-align "justify") (font-size-adjust "100%") (character-tracking "0") (hyphenate "true") (display ,(symbol->string (gensym)))) attrs) exprs)) - -(define-tag-function (hr attrs exprs) - hrbr) - -(define-tag-function (blockquote attrs exprs) - (qexpr (append '((display "block") - (first-line-indent "0") - (background-color "#eee") - (font-family "fira") (font-size "10") (line-height "14") - (border-width-top "0.5") (border-color-top "gray") (border-inset-top "8") - (border-width-left "3") (border-color-left "gray") (border-inset-left "20") - (border-width-bottom "0.5") (border-color-bottom "gray") (border-inset-bottom "-2") - (border-width-right "0.5") (border-color-right "gray") (border-inset-right "20") - (inset-top "10") (inset-bottom "8") (inset-left "30") (inset-right "30") - (keep-lines "yes")) - attrs) exprs)) - -(define id (default-tag-function 'id)) -(define class (default-tag-function 'class)) - -(define-tag-function (strong attrs exprs) - (qexpr (list* '(font-bold "true") '(font-size-adjust "100%") attrs) exprs)) - -(define-tag-function (a attrs exprs) - (qexpr `((link ,(cadr (assoc 'href attrs)))(color "MediumVioletRed")) exprs)) - -(define-tag-function (em attrs exprs) - (qexpr (list* '(font-italic "true") '(font-size-adjust "100%") attrs) exprs)) - -(define-syntax-rule (attr-list . attrs) 'attrs) - -(define (heading-base font-size attrs exprs) - (qexpr (append `((font-family "fira-light") (first-line-indent "0") (display "block") (font-size ,(number->string font-size))(line-height ,(number->string (* 1.2 font-size))) (border-width-top "0.5")(border-inset-top "9") (inset-bottom "-3") (inset-top "6") (keep-with-next "true")) attrs) exprs)) - -(define-tag-function (h1 attrs exprs) - (heading-base 20 (append '() attrs) exprs)) - -(define-tag-function (h2 attrs exprs) (heading-base 16 attrs exprs)) -(define-tag-function (h3 attrs exprs) (heading-base 14 attrs exprs)) - -(define h4 h3) -(define h5 h3) -(define h6 h3) - -(define-tag-function (code attrs exprs) - (qexpr (append '((font-family "fira-mono")#;(line-align "right")(font-size "10")(bg "aliceblue")) attrs) exprs)) - -(define-tag-function (pre attrs exprs) - ;; pre needs to convert white space to equivalent layout elements - (define new-exprs (add-between - (for*/list ([expr (in-list exprs)] - [str (in-list (string-split (string-join (get-elements expr) "") "\n"))]) - `(,(get-tag expr) ,(get-attrs expr) ,(string-replace str " " " "))) - lbr)) - (qexpr (list* '(display "block") '(background-color "aliceblue") - '(first-line-indent "0") - '(font-family "fira-mono") '(font-size "11") '(line-height "14") - '(border-inset-top "10") - '(border-width-left "2") '(border-color-left "#669") '(border-inset-left "0") - '(border-inset-bottom "-4") - '(inset-left "12") '(inset-top "12") '(inset-bottom "8") - attrs) new-exprs)) - +(require (for-syntax racket/base) + racket/runtime-path + racket/path + racket/string + racket/promise + racket/match + racket/list + sugar/list + racket/date + pitfall + quad + sugar/debug + racket/unsafe/ops + hyphenate) + +(provide hrbr lbr pbr run default-font-size default-font-face) (define draw-debug? #f) (define draw-debug-line? #t) (define draw-debug-block? #t) -(define draw-debug-string? #f) - -(require racket/dict) -(define (list-base attrs exprs [bullet-val #f]) - (define bullet-space-factor 2.5) - (define em (dict-ref attrs 'font-size default-font-size)) - (define bullet-indent (* bullet-space-factor em)) - (qexpr (list* `(inset-left ,(number->string bullet-indent)) attrs) - (add-between - (for/list ([(expr idx) (in-indexed exprs)]) - (list* (get-tag expr) (cons (list 'list-index (or bullet-val (format "~a" (add1 idx)))) (get-attrs expr)) (get-elements expr))) - pbr))) - -(define-tag-function (ol attrs exprs) (list-base attrs exprs)) -(define-tag-function (ul attrs exprs) (list-base attrs exprs "•")) -(define-tag-function (li attrs exprs) (qexpr attrs exprs)) +(define draw-debug-string? #t) (define-quad string-quad quad ()) (define q:string (q #:type string-quad @@ -125,21 +36,21 @@ ;; draw with pdf text routine #:draw (λ (q doc) (when (pair? (quad-elems q)) - (font doc (path->string (quad-ref q font-path-key))) + (font doc (path->string (quad-ref q font-path-key default-font-face))) (font-size doc (quad-ref q 'font-size 12)) (fill-color doc (quad-ref q 'color "black")) (define str (unsafe-car (quad-elems q))) (match-define (list x y) (quad-origin q)) (text doc str x y #:tracking (quad-ref q 'character-tracking 0) - #:bg (quad-ref q 'bg #f) + #:bg (quad-ref q 'bg) #:features (list (cons #"tnum" 1)) - #:link (quad-ref q 'link #f)))) + #:link (quad-ref q 'link)))) #:draw-end (if draw-debug-string? (λ (q doc) (draw-debug q doc "#99f" "#ccf")) void))) -(define-runtime-path default-font-face "fonts/charter/charter.otf") +(define-runtime-path default-font-face "fonts/charter.otf") (define default-font-family "charter") (define default-font-size 12) @@ -293,7 +204,6 @@ [substr (in-list (regexp-match* (regexp (string hyphen-char)) hstr #:gap-select? #t))]) (struct-copy quad q [elems (list substr)]))])))) -(require sugar/list) (define-quad filler quad ()) (define (fill-wrap qs ending-q line-q) (match (and (pair? qs) (quad-ref (car qs) (if ending-q @@ -472,7 +382,6 @@ (define side-margin (/ 120 (if zoom-mode? zoom-scale 1))) (define page-offset (pt (/ side-margin (if zoom-mode? 3 1)) (/ top-margin (if zoom-mode? 3 1)))) -(require racket/date) (define q:page (q #:offset page-offset #:draw-start (λ (q doc) (add-page doc) (scale doc (if zoom-mode? zoom-scale 1) (if zoom-mode? zoom-scale 1))) @@ -690,7 +599,8 @@ (setup-font-path-table! pdf-path) (parameterize ([current-doc pdf] [verbose-quad-printing? #false]) - (let* ([x (time-name parse-qexpr (qexpr->quad xs))] + (let* ([x (time-name parse-qexpr (qexpr->quad `(q ((font-family ,default-font-family) + (font-size ,(number->string default-font-size))) ,xs)))] [x (time-name atomize (atomize x #:attrs-proc handle-cascading-attrs))] [x (time-name hyphenate (handle-hyphenate x))] [x (time-name ->string-quad (map ->string-quad x))] @@ -700,47 +610,3 @@ [x (time-name page-wrap (page-wrap x vertical-height pdf-path))] [x (time-name position (position (struct-copy quad q:doc [elems x])))]) (time-name draw (draw x pdf))))) - -(define-syntax (mb stx) - (syntax-case stx () - [(_ PDF-PATH . STRS) - #'(#%module-begin - ;; stick an nbsp in the strings so we have one printing char - (define strs (match (list . STRS) - [(? null?) '(" ")] - [strs strs])) - (define qx (root null (add-between strs (list pbr) - #:before-first (list pbr) - #:after-last (list pbr) - #:splice? #true))) - (run qx PDF-PATH))])) - -(module+ reader - (require scribble/reader syntax/strip-context (only-in markdown parse-markdown) - racket/match txexpr) - (provide (rename-out [quad-read-syntax read-syntax])) - - - (define (xexpr->parse-tree x) - ;; an ordinary txexpr can't serve as a parse tree because of the attrs list fails when passed to #%app. - ;; so stick an `attr-list` identifier on it which can hook into the expander. - ;; sort of SXML-ish. - (let loop ([x x]) - (match x - [(txexpr tag attrs elems) (list* tag (cons 'attr-list attrs) (map loop elems))] - [(? list? xs) (map loop xs)] - [_ x]))) - - (define (quad-read-syntax path-string p) - (define quad-at-reader (make-at-reader - #:syntax? #t - #:inside? #t - #:command-char #\◊)) - (define stx (quad-at-reader path-string p)) - (define parsed-stxs (datum->syntax stx (xexpr->parse-tree (parse-markdown (apply string-append (syntax->datum stx)))))) - (strip-context - (with-syntax ([STXS parsed-stxs] - [PDF-PATH (path-replace-extension path-string #".pdf")]) - #'(module _ qtest/markdown - PDF-PATH - . STXS))))) diff --git a/quad/quadwriter/fonts/charter.otf b/quad/quadwriter/fonts/charter.otf new file mode 100644 index 0000000000000000000000000000000000000000..d2314d35b4d86293a74fab34e67183938b73e6d2 GIT binary patch literal 26968 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 (gensym)))) attrs) exprs)) + +(define-tag-function (hr attrs exprs) + hrbr) + +(define-tag-function (blockquote attrs exprs) + (qexpr (append '((display "block") + (first-line-indent "0") + (background-color "#eee") + (font-family "fira") (font-size "10") (line-height "14") + (border-width-top "0.5") (border-color-top "gray") (border-inset-top "8") + (border-width-left "3") (border-color-left "gray") (border-inset-left "20") + (border-width-bottom "0.5") (border-color-bottom "gray") (border-inset-bottom "-2") + (border-width-right "0.5") (border-color-right "gray") (border-inset-right "20") + (inset-top "10") (inset-bottom "8") (inset-left "30") (inset-right "30") + (keep-lines "yes")) + attrs) exprs)) + +(define id (default-tag-function 'id)) +(define class (default-tag-function 'class)) + +(define-tag-function (strong attrs exprs) + (qexpr (list* '(font-bold "true") '(font-size-adjust "100%") attrs) exprs)) + +(define-tag-function (a attrs exprs) + (qexpr `((link ,(cadr (assoc 'href attrs)))(color "MediumVioletRed")) exprs)) + +(define-tag-function (em attrs exprs) + (qexpr (list* '(font-italic "true") '(font-size-adjust "100%") attrs) exprs)) + +(define-syntax-rule (attr-list . attrs) 'attrs) + +(define (heading-base font-size attrs exprs) + (qexpr (append `((font-family "fira-light") (first-line-indent "0") (display "block") (font-size ,(number->string font-size))(line-height ,(number->string (* 1.2 font-size))) (border-width-top "0.5")(border-inset-top "9") (inset-bottom "-3") (inset-top "6") (keep-with-next "true")) attrs) exprs)) + +(define-tag-function (h1 attrs exprs) + (heading-base 20 (append '() attrs) exprs)) + +(define-tag-function (h2 attrs exprs) (heading-base 16 attrs exprs)) +(define-tag-function (h3 attrs exprs) (heading-base 14 attrs exprs)) + +(define h4 h3) +(define h5 h3) +(define h6 h3) + +(define-tag-function (code attrs exprs) + (qexpr (append '((font-family "fira-mono")#;(line-align "right")(font-size "10")(bg "aliceblue")) attrs) exprs)) + +(define-tag-function (pre attrs exprs) + ;; pre needs to convert white space to equivalent layout elements + (define new-exprs (add-between + (for*/list ([expr (in-list exprs)] + [str (in-list (string-split (string-join (get-elements expr) "") "\n"))]) + `(,(get-tag expr) ,(get-attrs expr) ,(string-replace str " " " "))) + lbr)) + (qexpr (list* '(display "block") '(background-color "aliceblue") + '(first-line-indent "0") + '(font-family "fira-mono") '(font-size "11") '(line-height "14") + '(border-inset-top "10") + '(border-width-left "2") '(border-color-left "#669") '(border-inset-left "0") + '(border-inset-bottom "-4") + '(inset-left "12") '(inset-top "12") '(inset-bottom "8") + attrs) new-exprs)) + +(define (list-base attrs exprs [bullet-val #f]) + (define bullet-space-factor 2.5) + (define em (dict-ref attrs 'font-size default-font-size)) + (define bullet-indent (* bullet-space-factor em)) + (qexpr (list* `(inset-left ,(number->string bullet-indent)) attrs) + (add-between + (for/list ([(expr idx) (in-indexed exprs)]) + (list* (get-tag expr) (cons (list 'list-index (or bullet-val (format "~a" (add1 idx)))) (get-attrs expr)) (get-elements expr))) + pbr))) + +(define-tag-function (ol attrs exprs) (list-base attrs exprs)) +(define-tag-function (ul attrs exprs) (list-base attrs exprs "•")) +(define-tag-function (li attrs exprs) (qexpr attrs exprs)) + +(define-syntax (mb stx) + (syntax-case stx () + [(_ PDF-PATH . STRS) + #'(#%module-begin + ;; stick an nbsp in the strings so we have one printing char + (define strs (match (list . STRS) + [(? null?) '(" ")] + [strs strs])) + (define qx (root null (add-between strs (list pbr) + #:before-first (list pbr) + #:after-last (list pbr) + #:splice? #true))) + (run qx PDF-PATH))])) + +(module reader racket/base + (require scribble/reader syntax/strip-context (only-in markdown parse-markdown) + racket/match txexpr) + (provide (rename-out [quad-read-syntax read-syntax])) + + + (define (xexpr->parse-tree x) + ;; an ordinary txexpr can't serve as a parse tree because of the attrs list fails when passed to #%app. + ;; so stick an `attr-list` identifier on it which can hook into the expander. + ;; sort of SXML-ish. + (let loop ([x x]) + (match x + [(txexpr tag attrs elems) (list* tag (cons 'attr-list attrs) (map loop elems))] + [(? list? xs) (map loop xs)] + [_ x]))) + + (define (quad-read-syntax path-string p) + (define quad-at-reader (make-at-reader + #:syntax? #t + #:inside? #t + #:command-char #\◊)) + (define stx (quad-at-reader path-string p)) + (define parsed-stxs (datum->syntax stx (xexpr->parse-tree (parse-markdown (apply string-append (syntax->datum stx)))))) + (strip-context + (with-syntax ([STXS parsed-stxs] + [PDF-PATH (path-replace-extension path-string #".pdf")]) + #'(module _ quadwriter/markdown + PDF-PATH + . STXS))))) \ No newline at end of file